animExportUtil/animExportUtil.cpp
 
 
 
animExportUtil/animExportUtil.cpp
//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

//
//      File Name:      animExportUtil.h
//
//      Description: an animation export utility which illustrates how to
//      use the MAnimUtil animation helper class, as well as how to export
//      animation using the Maya .anim format
//
//

// *****************************************************************************

// INCLUDED HEADER FILES

#include <maya/MFStream.h>
#include <stdlib.h>
#include <string.h>

#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MFnPlugin.h>
#include <maya/MPxFileTranslator.h>
#include <maya/MDagPath.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MObjectArray.h>
#include <maya/MItDag.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MFnAttribute.h>
#include <maya/MFnSet.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>

#include <maya/MFnAnimCurve.h>
#include <maya/MAnimUtil.h>

#include "animExportUtil.h"
#include "animFileExport.h"

#if defined (OSMac_)
using namespace std;
extern "C" int strcasecmp (const char *, const char *);
#endif
// *****************************************************************************

// HELPER METHODS

// *****************************************************************************

#ifndef min
static inline double
min (double a, double b)
{
        return (a < b ? a : b);
}
#endif

#ifndef max
static inline double
max (double a, double b)
{
        return (a > b ? a : b);
}
#endif

// *****************************************************************************

// PUBLIC MEMBER FUNCTIONS

TanimExportUtil::TanimExportUtil()
{
}

TanimExportUtil::~TanimExportUtil()
{
}

MStatus
TanimExportUtil::writer (
        const MFileObject &file,
        const MString &/*options*/,
        FileAccessMode mode
)
{
        // Create the export file
        //
        ofstream animFile (file.fullName().asChar());

        // Create a selection list to hold the objects we want to export
        //
        MSelectionList list;
        if (mode == kExportActiveAccessMode) {
                // Use the active list
                //
                MGlobal::getActiveSelectionList (list);
        }
        else {
                // Create a selection list with all the objects in the world
                //
                // Add the top level dag objects
                //
                MItDag dagIt (MItDag::kBreadthFirst);
                for (dagIt.next(); !dagIt.isDone(); dagIt.next()) {
                        MDagPath path;
                        if (dagIt.getPath (path) != MS::kSuccess) {
                                continue;
                        }
                        list.add (path);
                        // We only want the top level objects (since we will walk
                        // down the hierarchy later
                        //
                        dagIt.prune ();
                }
                // Gather the rest of the dependency nodes
                //
                MItDependencyNodes nodeIt;
                for (; !nodeIt.isDone(); nodeIt.next()) {
                        MObject node = nodeIt.item();
                        if (node.isNull()) {
                                continue;
                        }
                        // We have already saved the dag objects, so skip them here
                        //
                        if (node.hasFn (MFn::kDagNode)) {
                                continue;
                        }
                        // Watch out for characters, and only write the top level
                        // character
                        //
                        if (node.hasFn (MFn::kCharacter)) {
                                // Find out which (if any) sets this node belongs to
                                //
                                // Get the message attribute
                                //
                                MFnDependencyNode fnNode (node);
                                MObject aMessage = fnNode.attribute (MString ("message"));
                                MPlug messagePlug (node, aMessage);
                                // Now find what it is connected to as a source plug
                                //
                                MPlugArray srcPlugArray;
                                messagePlug.connectedTo (srcPlugArray, false, true);
                                // Now walk through each connection and see if any is to
                                // another character
                                //
                                unsigned int numPlugs = srcPlugArray.length();
                                bool belongsToCharacter = false;
                                for (unsigned int i = 0; (i < numPlugs) && !belongsToCharacter; i++) {
                                        const MPlug &plug = srcPlugArray[i];
                                        if (!plug.node().hasFn (MFn::kCharacter)) {
                                                continue;
                                        }
                                        belongsToCharacter = true;
                                }
                                if (!belongsToCharacter) {
                                        list.add (node);
                                }
                                continue;
                        }
                        // A superfluous test to filter out unanimated objects, just to
                        // show how to use MAnimUtil
                        //
                        if (!MAnimUtil::isAnimated (node)) {
                                continue;
                        }
                        list.add (node);
                }
        }

        // The Maya .anim format needs to know the bounds of the animation
        // since it could be used in pasteKey operations (which support
        // scaling in time), so gather that information now
        //
        // Find all the plugs that are animated in our selection list
        //
        MPlugArray animatedPlugs;
        MAnimUtil::findAnimatedPlugs (list, animatedPlugs);
        unsigned int numPlugs = animatedPlugs.length();
        bool hasTime = false;
        double startTime = 0.0;
        double endTime = 0.0;
        bool hasUnitless = false;
        double startUnitless = 0.0;
        double endUnitless = 0.0;
        unsigned int i;
        // For each animated plug, determine the bounds of the animation
        //
        for (i = 0; i < numPlugs; i++) {
                MPlug plug = animatedPlugs[i];
                MObjectArray animation;
                // Find the animation curve(s) that animate this plug
                //
                if (!MAnimUtil::findAnimation (plug, animation)) {
                        continue;
                }
                unsigned int numCurves = animation.length();
                for (unsigned int j = 0; j < numCurves; j++) {
                        MObject animCurveNode = animation[j];
                        if (!animCurveNode.hasFn (MFn::kAnimCurve)) {
                                continue;
                        }
                        MFnAnimCurve animCurve (animCurveNode);
                        unsigned int numKeys = animCurve.numKeys();
                        if (numKeys == 0) {
                                continue;
                        }
                        if (animCurve.isUnitlessInput()) {
                                if (!hasUnitless) {
                                        startUnitless = animCurve.unitlessInput (0);
                                        endUnitless = animCurve.unitlessInput (numKeys - 1);
                                        hasUnitless = true;
                                }
                                else {
                                        startUnitless = min (startUnitless, animCurve.unitlessInput (0));
                                        endUnitless = max (endUnitless, animCurve.unitlessInput (numKeys - 1));
                                }
                        }
                        else {
                                if (!hasTime) {
                                        startTime = animCurve.time (0).value();
                                        endTime = animCurve.time (numKeys - 1).value();
                                        hasTime = true;
                                }
                                else {
                                        startTime = min (startTime, animCurve.time (0).value());
                                        endTime = max (endTime, animCurve.time (numKeys - 1).value());
                                }
                        }
                }
        }

        // Write out the header information
        //
        animWriter writer;
        writer.writeHeader (animFile, startTime, endTime, startUnitless, endUnitless);

        // Now write out the animation for each object on the selection list
        //
        unsigned int numObjects = list.length();
        for (i = 0; i < numObjects; i++) {
                MDagPath path;
                MObject node;
                if (list.getDagPath (i, path) == MS::kSuccess) {
                        write (animFile, path);
                }
                else if (list.getDependNode (i, node) == MS::kSuccess) {
                        write (animFile, node);
                }
        }

        animFile.flush();
        animFile.close();

        return (MS::kSuccess);
}

void
TanimExportUtil::write (ofstream &animFile, const MDagPath &path)
{
        // Walk through the dag breadth first
        //
        MItDag dagIt (MItDag::kDepthFirst);
        dagIt.reset (path, MItDag::kDepthFirst);
        for (; !dagIt.isDone(); dagIt.next()) {
                MDagPath thisPath;
                if (dagIt.getPath (thisPath) != MS::kSuccess) {
                        continue;
                }
                // Find the animated plugs for this object
                //
                MPlugArray animatedPlugs;
                MObject node = thisPath.node();
                MFnDependencyNode fnNode (node);
                MAnimUtil::findAnimatedPlugs (thisPath, animatedPlugs);
                unsigned int numPlugs = animatedPlugs.length();
                if (numPlugs == 0) {
                        // If the object is not animated, then write out place holder
                        // information
                        //
                        animFile << "anim " << fnNode.name().asChar() << " " << dagIt.depth() << " " << thisPath.childCount() << " 0;\n";
                }
                else {
                        // Otherwise write out each animation curve
                        //
                        writeAnimatedPlugs (animFile, animatedPlugs, fnNode.name(), dagIt.depth(), thisPath.childCount());
                }
        }
}

void
TanimExportUtil::write (ofstream &animFile, const MObject &node)
{
        // Watch out for characters and handle them a little differently
        //
        if (node.hasFn (MFn::kCharacter)) {
                MObjectArray characterList;
                characterList.append (node);
                MIntArray depths;
                depths.append (0);
                unsigned int current = 0;
                while (current < characterList.length()) {
                        const MObject &thisNode = characterList[current];
                        int thisDepth = depths[current++];
                        // If this node is a character, then check for any immediate
                        // subCharacters
                        //
                        MFnSet fnSet (thisNode);
                        // Now find the set members
                        //
                        MSelectionList members;
                        fnSet.getMembers (members, false);
                        unsigned int childCount = 0;
                        MItSelectionList iter (members, MFn::kCharacter);
                        for (; !iter.isDone(); iter.next()) {
                                MObject childNode;
                                iter.getDependNode (childNode);
                                characterList.insert (childNode, current + childCount);
                                depths.insert (thisDepth + 1, current + childCount);
                                childCount++;
                        }
                        // Find the animated plugs for this object
                        //
                        MPlugArray animatedPlugs;
                        MAnimUtil::findAnimatedPlugs (thisNode, animatedPlugs);
                        unsigned int numPlugs = animatedPlugs.length();
                        if (numPlugs == 0) {
                                // If the object is not animated, then write out place holder
                                // information
                                //
                                animFile << "anim " << fnSet.name().asChar() << " " << thisDepth << " " << childCount << " 0;\n";
                        }
                        else {
                                // Otherwise write out each animation curve
                                //
                                writeAnimatedPlugs (animFile, animatedPlugs, fnSet.name(), thisDepth, childCount);
                        }
                }
                return;
        }
        // Find the animated plugs for this object
        //
        MPlugArray animatedPlugs;
        MFnDependencyNode fnNode (node);
        MAnimUtil::findAnimatedPlugs (node, animatedPlugs);
        unsigned int numPlugs = animatedPlugs.length();
        if (numPlugs != 0) {
                // If the object is animated the write out each animation curve
                //
                writeAnimatedPlugs (animFile, animatedPlugs, fnNode.name(), 0, 0);
        }
}

void
TanimExportUtil::writeAnimatedPlugs (
        ofstream &animFile,
        const MPlugArray &animatedPlugs,
        const MString &nodeName,
        unsigned int depth,
        unsigned int childCount
)
{
        // Walk through each animated plug and write out the animation curve(s)
        //
        unsigned int numPlugs = animatedPlugs.length();
        for (unsigned int i = 0; i < numPlugs; i++) {
                MPlug plug = animatedPlugs[i];
                MObjectArray animation;
                if (!MAnimUtil::findAnimation (plug, animation)) {
                        continue;
                }
                // Write out the plugs' anim statement
                //
                animFile << "anim ";
                // build up the full attribute name
                //
                MPlug attrPlug (plug);
                MObject attrObj = attrPlug.attribute();
                MFnAttribute fnAttr (attrObj);
                MString fullAttrName (fnAttr.name());
                attrPlug = attrPlug.parent();
                while (!attrPlug.isNull()) {
                        attrObj = attrPlug.attribute();
                        MFnAttribute fnAttr2 (attrObj);
                        fullAttrName = fnAttr2.name() + "." + fullAttrName;
                        attrPlug = attrPlug.parent();
                }
                attrObj = plug.attribute();
                MFnAttribute fnLeafAttr (attrObj);
                animFile << fullAttrName.asChar() << " " << fnLeafAttr.name().asChar() << " " << nodeName.asChar() << " " << depth << " " << childCount << " " << i << ";\n";
                unsigned int numCurves = animation.length();
                // Write out each animation curve that animates the plug
                //
                for (unsigned int j = 0; j < numCurves; j++) {
                        MObject animCurveNode = animation[j];
                        if (!animCurveNode.hasFn (MFn::kAnimCurve)) {
                                continue;
                        }
                        animWriter writer;
                        writer.writeAnimCurve (animFile, &animCurveNode);
                }
        }
}

bool
TanimExportUtil::haveWriteMethod () const
{
        return (true);
}

MString
TanimExportUtil::defaultExtension () const
{
        return (MString("anim"));
}

MPxFileTranslator::MFileKind
TanimExportUtil::identifyFile (
        const MFileObject &file,
        const char * /*buffer*/,
        short /*size*/
) const
{
        const char *name = file.name().asChar();
        int   nameLength = (int)strlen(name);

        if ((nameLength > 5) && !strcasecmp(name+nameLength-5, ".anim")) {
                return (kIsMyFileType);
        }

        return (kNotMyFileType);
}

/* static */ void *
TanimExportUtil::creator ()
{
        return (new TanimExportUtil);
}

//--------------------------------------------------------------------------------
//      Plugin management
//--------------------------------------------------------------------------------

MStatus
initializePlugin (MObject obj)
{
        MStatus status;
        MFnPlugin plugin (obj, PLUGIN_COMPANY, "3.0");
        status = plugin.registerFileTranslator ("animExportUtil", "", TanimExportUtil::creator);
        return (status);
}

MStatus
uninitializePlugin (MObject obj)
{
        MFnPlugin plugin (obj);
        plugin.deregisterFileTranslator ("animExportUtil");
        return (MS::kSuccess);
}