- Main Page
- Related Pages
- Modules
- Classes
- Examples

# 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);
    }