Package com.ardor3d.extension.animation.skeletal

Source Code of com.ardor3d.extension.animation.skeletal.SkeletonPose

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.extension.animation.skeletal;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

import com.ardor3d.annotation.SavableFactory;
import com.ardor3d.math.Matrix4;
import com.ardor3d.math.Transform;
import com.ardor3d.util.export.CapsuleUtils;
import com.ardor3d.util.export.InputCapsule;
import com.ardor3d.util.export.OutputCapsule;
import com.ardor3d.util.export.Savable;
import com.google.common.collect.Lists;

/**
* Joins a Skeleton with an array of joint poses. This allows the skeleton to exist and be reused between multiple
* instances of poses.
*/
@SavableFactory(factoryMethod = "initSavable")
public class SkeletonPose implements Savable {

    /** The skeleton being "posed". */
    private final Skeleton _skeleton;

    /** Local transforms for the joints of the associated skeleton. */
    private final Transform[] _localTransforms;

    /** Global transforms for the joints of the associated skeleton. Not saved to savable. */
    private transient final Transform[] _globalTransforms;

    /**
     * A palette of matrices used in skin deformation - basically the global transform X the inverse bind pose
     * transform. Not saved to savable.
     */
    private transient final Matrix4[] _matrixPalette;

    /**
     * The list of elements interested in notification when this SkeletonPose updates. Not saved to savable.
     */
    private transient final List<PoseListener> _poseListeners = Lists.newArrayListWithCapacity(1);

    /**
     * Construct a new SkeletonPose using the given Skeleton.
     *
     * @param skeleton
     *            the skeleton to use.
     */
    public SkeletonPose(final Skeleton skeleton) {
        assert skeleton != null : "skeleton must not be null.";

        _skeleton = skeleton;
        final int jointCount = _skeleton.getJoints().length;

        // init local transforms
        _localTransforms = new Transform[jointCount];
        for (int i = 0; i < jointCount; i++) {
            _localTransforms[i] = new Transform();
        }

        // init global transforms
        _globalTransforms = new Transform[jointCount];
        for (int i = 0; i < jointCount; i++) {
            _globalTransforms[i] = new Transform();
        }

        // init palette
        _matrixPalette = new Matrix4[jointCount];
        for (int i = 0; i < jointCount; i++) {
            _matrixPalette[i] = new Matrix4();
        }

        // start off in bind pose.
        setToBindPose();
    }

    /**
     * @return the skeleton posed by this object.
     */
    public Skeleton getSkeleton() {
        return _skeleton;
    }

    /**
     * @return an array of local space transforms for each of the skeleton's joints.
     */
    public Transform[] getLocalJointTransforms() {
        return _localTransforms;
    }

    /**
     * @return an array of global space transforms for each of the skeleton's joints. This does not take into account
     *         any transformation of the SkeletonMesh using the pose.
     */
    public Transform[] getGlobalJointTransforms() {
        return _globalTransforms;
    }

    /**
     * @return an array of global space transforms for each of the skeleton's joints.
     */
    public Matrix4[] getMatrixPalette() {
        return _matrixPalette;
    }

    /**
     * Register a PoseListener on this SkeletonPose.
     *
     * @param listener
     *            the PoseListener
     */
    public void addPoseListener(final PoseListener listener) {
        _poseListeners.add(listener);
    }

    /**
     * Remove a PoseListener from this SkeletonPose.
     *
     * @param listener
     *            the PoseListener
     */
    public void removePoseListener(final PoseListener listener) {
        _poseListeners.remove(listener);
    }

    /**
     * Clear all PoseListeners registered on this SkeletonPose.
     */
    public void clearListeners() {
        _poseListeners.clear();
    }

    /**
     * Update the global and palette transforms of our posed joints based on the current local joint transforms.
     */
    public void updateTransforms() {
        final Transform temp = Transform.fetchTempInstance();
        // we go in update array order, which ensures parent global transforms are updated before child.
        // final int[] orders = _skeleton.getJointOrders();
        final int nrJoints = _skeleton.getJoints().length;
        // for (int i = 0; i < orders.length; i++) {
        for (int i = 0; i < nrJoints; i++) {
            // the joint index
            final int index = i;

            // find our parent
            final short parentIndex = _skeleton.getJoints()[index].getParentIndex();
            if (parentIndex != Joint.NO_PARENT) {
                // we have a parent, so take us from local->parent->model space by multiplying by parent's local->model
                // space transform.
                _globalTransforms[parentIndex].multiply(_localTransforms[index], _globalTransforms[index]);
            } else {
                // no parent so just set global to the local transform
                _globalTransforms[index].set(_localTransforms[index]);
            }

            // at this point we have a local->model space transform for this joint, for skinning we multiply this by the
            // joint's inverse bind pose (joint->model space, inverted). This gives us a transform that can take a
            // vertex from bind pose (model space) to current pose (model space).
            _globalTransforms[index].multiply(_skeleton.getJoints()[index].getInverseBindPose(), temp);
            temp.getHomogeneousMatrix(_matrixPalette[index]);
        }
        Transform.releaseTempInstance(temp);
        firePoseUpdated();
    }

    /**
     * Update our local joint transforms so that they reflect the skeleton in bind pose.
     */
    public void setToBindPose() {
        final Transform temp = Transform.fetchTempInstance();
        // go through our local transforms
        for (int i = 0; i < _localTransforms.length; i++) {
            // Set us to the bind pose
            _localTransforms[i].set(_skeleton.getJoints()[i].getInverseBindPose());
            // then invert.
            _localTransforms[i].invert(_localTransforms[i]);

            // At this point we are in model space, so we need to remove our parent's transform (if we have one.)
            final short parentIndex = _skeleton.getJoints()[i].getParentIndex();
            if (parentIndex != Joint.NO_PARENT) {
                // We remove the parent's transform simply by multiplying by its inverse bind pose. Done! :)
                _skeleton.getJoints()[parentIndex].getInverseBindPose().multiply(_localTransforms[i], temp);
                _localTransforms[i].set(temp);
            }
        }
        Transform.releaseTempInstance(temp);
        updateTransforms();
    }

    /**
     * Notify any registered PoseListeners that this pose has been "updated".
     */
    public void firePoseUpdated() {
        for (int i = _poseListeners.size(); --i >= 0;) {
            // Pull out pose
            final PoseListener listener = _poseListeners.get(i);

            // notify
            listener.poseUpdated(this);
        }
    }

    public SkeletonPose makeCopy() {
        final SkeletonPose copy = new SkeletonPose(_skeleton);

        int i = 0;
        for (final Transform t : _localTransforms) {
            copy._localTransforms[i++] = t.clone();
        }
        i = 0;
        for (final Transform t : _globalTransforms) {
            copy._globalTransforms[i++] = t.clone();
        }
        i = 0;
        for (final Matrix4 m : _matrixPalette) {
            copy._matrixPalette[i++] = m.clone();
        }

        return copy;
    }

    // /////////////////
    // Methods for Savable
    // /////////////////

    public Class<? extends SkeletonPose> getClassTag() {
        return this.getClass();
    }

    public void write(final OutputCapsule capsule) throws IOException {
        capsule.write(_skeleton, "skeleton", null);
        capsule.write(_localTransforms, "localTransforms", null);
    }

    public void read(final InputCapsule capsule) throws IOException {
        final Skeleton skeleton = (Skeleton) capsule.readSavable("skeleton", null);
        final Transform[] localTransforms = CapsuleUtils.asArray(capsule.readSavableArray("localTransforms", null),
                Transform.class);
        try {
            final Field field1 = SkeletonPose.class.getDeclaredField("_skeleton");
            field1.setAccessible(true);
            field1.set(this, skeleton);

            final int jointCount = _skeleton.getJoints().length;

            // init local transforms
            final Field field2 = SkeletonPose.class.getDeclaredField("_localTransforms");
            field2.setAccessible(true);
            field2.set(this, localTransforms);

            // init global transforms
            final Transform[] globalTransforms = new Transform[jointCount];
            for (int i = 0; i < jointCount; i++) {
                globalTransforms[i] = new Transform();
            }
            final Field field3 = SkeletonPose.class.getDeclaredField("_globalTransforms");
            field3.setAccessible(true);
            field3.set(this, globalTransforms);

            // init palette
            final Matrix4[] matrixPalette = new Matrix4[jointCount];
            for (int i = 0; i < jointCount; i++) {
                matrixPalette[i] = new Matrix4();
            }
            final Field field4 = SkeletonPose.class.getDeclaredField("_matrixPalette");
            field4.setAccessible(true);
            field4.set(this, matrixPalette);

            updateTransforms();
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }

    public static SkeletonPose initSavable() {
        return new SkeletonPose();
    }

    protected SkeletonPose() {
        _skeleton = null;
        _localTransforms = null;
        _globalTransforms = null;
        _matrixPalette = null;
    }
}
TOP

Related Classes of com.ardor3d.extension.animation.skeletal.SkeletonPose

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.