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

# animImportExport.cpp

    //-
    // ==========================================================================
    // Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All
    // rights reserved.
    //
    // The coded instructions, statements, computer programs, and/or related
    // material (collectively the "Data") in these files contain unpublished
    // information proprietary to Autodesk, Inc. ("Autodesk") and/or its
    // licensors, which is protected by U.S. and Canadian federal copyright
    // law and by international treaties.
    //
    // The Data is provided for use exclusively by You. You have the right
    // to use, modify, and incorporate this Data into other products for
    // purposes authorized by the Autodesk software license agreement,
    // without fee.
    //
    // The copyright notices in the Software and this entire statement,
    // including the above license grant, this restriction and the
    // following disclaimer, must be included in all copies of the
    // Software, in whole or in part, and all derivative works of
    // the Software, unless such copies or derivative works are solely
    // in the form of machine-executable object code generated by a
    // source language processor.
    //
    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
    // AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
    // WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
    // NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
    // PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
    // TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
    // BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
    // DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
    // AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
    // OR PROBABILITY OF SUCH DAMAGES.
    //
    // ==========================================================================
    //+

    //
    //  File Name:  animImport.cc
    //
    //  Description:
    //      Imports a .anim file into anim curves attached to the selected
    //      Maya objects.
    //

    #include <stdlib.h>
    #include <string.h>

    #include <maya/MFStream.h>
    #include <maya/MGlobal.h>
    #include <maya/MString.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MPxFileTranslator.h>

    #include <maya/MSelectionList.h>

    #include <maya/MFnAnimCurve.h>
    #include <maya/MAnimCurveClipboard.h>
    #include <maya/MAnimCurveClipboardItem.h>
    #include <maya/MAnimCurveClipboardItemArray.h>

    #include "animImportExport.h"
    #include "animFileUtils.h"
    #include "animImportExportStrings.h"

    #if defined (OSMac_)
    #   include <sys/param.h>
    extern "C" int strcasecmp (const char *, const char *);
    #endif
    //-----------------------------------------------------------------------------
    //  anim Importer
    //-----------------------------------------------------------------------------

    const char *const animImportOptionScript = "animImportOptions";
    const char *const animImportDefaultOptions =
        "targetTime=4;copies=1;option=replace;pictures=0;connect=0;";

    // Register all strings used by the plugin C++ code
    static MStatus registerMStringResources(void)
    {
        MStringResource::registerString(kNothingSelected);
        MStringResource::registerString(kPasteFailed);
        MStringResource::registerString(kAnimCurveNotFound);
        MStringResource::registerString(kInvalidAngleUnits);
        MStringResource::registerString(kInvalidLinearUnits);
        MStringResource::registerString(kInvalidTimeUnits);
        MStringResource::registerString(kInvalidVersion);
        MStringResource::registerString(kSettingToUnit);
        MStringResource::registerString(kMissingKeyword);
        MStringResource::registerString(kCouldNotReadAnim);
        MStringResource::registerString(kCouldNotCreateAnim);
        MStringResource::registerString(kUnknownKeyword);
        MStringResource::registerString(kClipboardFailure);
        MStringResource::registerString(kSettingTanAngleUnit);
        MStringResource::registerString(kUnknownNode);
        MStringResource::registerString(kCouldNotKey);
        MStringResource::registerString(kMissingBrace);
        MStringResource::registerString(kCouldNotExport);
        return MS::kSuccess;
    }

    animImport::animImport()
    : MPxFileTranslator()
    {
    }

    animImport::~animImport()
    {
    }

    void *animImport::creator()
    {
        return new animImport();
    }

    MStatus animImport::reader( const MFileObject& file,
                                    const MString& options,
                                    FileAccessMode mode)
    {
        MStatus status = MS::kFailure;

        MString fileName = file.fullName();
    #if defined (OSMac_)
        char fname[MAXPATHLEN];
        strcpy (fname, fileName.asChar());
        ifstream animFile(fname);
    #else
        ifstream animFile(fileName.asChar());
    #endif
        //  Parse the options. The options syntax is in the form of
        //  "flag=val;flag1=val;flag2=val"
        //
        MString pasteFlags;
        if (options.length() > 0) {
            //  Set up the flags for the paste command.
            //
            const MString flagTargetTime("targetTime");
            const MString flagTime("time");
            const MString flagCopies("copies");
            const MString flagOption("option");
            const MString flagConnect("connect");

            MString copyValue;
            MString flagValue;
            MString connectValue;
            MString timeValue;

            //  Start parsing.
            //
            MStringArray optionList;
            MStringArray theOption;
            options.split(';', optionList);

            unsigned nOptions = optionList.length();
            for (unsigned i = 0; i < nOptions; i++) {

                theOption.clear();
                optionList[i].split('=', theOption);
                if (theOption.length() < 1) {
                    continue;
                }

                if (theOption[0] == flagCopies && theOption.length() > 1) {
                    copyValue = theOption[1];;
                } else if (theOption[0] == flagOption && theOption.length() > 1) {
                    flagValue = theOption[1];
                } else if (theOption[0] == flagConnect && theOption.length() > 1) {
                    if (theOption[1].asInt() != 0) {
                        connectValue += theOption[1];
                    }
                } else if (theOption[0] == flagTime && theOption.length() > 1) {
                    timeValue += theOption[1];
                }
            }

            if (copyValue.length() > 0) {
                pasteFlags += " -copies ";
                pasteFlags += copyValue;
                pasteFlags += " ";
            }
            if (flagValue.length() > 0) {
                pasteFlags += " -option \"";
                pasteFlags += flagValue;
                pasteFlags += "\" ";
            }
            if (connectValue.length() > 0) {
                pasteFlags += " -connect ";
                pasteFlags += connectValue;
                pasteFlags += " ";
            }
            if (timeValue.length() > 0) {
                bool useQuotes = !timeValue.isDouble();
                pasteFlags += " -time ";
                if (useQuotes) pasteFlags += "\"";
                pasteFlags += timeValue;
                if (useQuotes) pasteFlags += "\"";
                pasteFlags += " ";
            }
        }

        if (mode == kImportAccessMode) {
            status = importAnim(animFile, pasteFlags);
        }

        animFile.close();
        return status;
    }

    bool animImport::haveReadMethod() const
    {
        return true;
    }

    bool animImport::haveWriteMethod() const
    {
        return false;
    }

    bool animImport::canBeOpened() const
    {
        return false;
    }

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

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

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

        //  Check the buffer to see if this contains the correct keywords
        //  to be a anim file.
        //
        if (strncmp(buffer, "animVersion", 11) == 0) {
            return kIsMyFileType;
        }

        return  kNotMyFileType;
    }

    MStatus
    animImport::importAnim(ifstream &animFile, const MString &pasteFlags)
    {
        MStatus status = MS::kFailure;
        MAnimCurveClipboard::theAPIClipboard().clear();

        //  If the selection list is empty, there is nothing to import.
        //
        MSelectionList sList;
        MGlobal::getActiveSelectionList(sList);
        if (sList.isEmpty()) {
            MString msg = MStringResource::getString(kNothingSelected, status);
            MGlobal::displayError(msg);
            return (MS::kFailure);
        }

        if (MS::kSuccess !=
                (status = fReader.readClipboard(animFile,
                MAnimCurveClipboard::theAPIClipboard()))) {

            return status;
        }

        if (MAnimCurveClipboard::theAPIClipboard().isEmpty()) {
            return (MS::kFailure);
        }

            MString command("pasteKey -cb api ");
            command += pasteFlags;

            int result;
            if (MS::kSuccess != (status =
                MGlobal::executeCommand(command, result, false, true))) {
                MString msg = MStringResource::getString(kPasteFailed, status);
                MGlobal::displayError(msg);
                return status;
            }

        return status;
    }

    //-----------------------------------------------------------------------------
    //  anim Exporter
    //-----------------------------------------------------------------------------

    const char *const animExportOptionScript = "animExportOptions";
    const char *const animExportDefaultOptions = "precision=8;nodeNames=1;verboseUnits=0;whichRange=1;range=0:10;options=keys;hierarchy=none;controlPoints=0;shapes=1;helpPictures=0;useChannelBox=0;copyKeyCmd=";

    const int kDefaultPrecision = 8;    //  float precision.

    animExport::animExport()
    : MPxFileTranslator()
    {
    }

    animExport::~animExport()
    {
    }

    void *animExport::creator()
    {
        return new animExport();
    }

    MStatus animExport::writer( const MFileObject& file,
                                    const MString& options,
                                    FileAccessMode mode)
    {
        MStatus status = MS::kFailure;

        MString fileName = file.fullName();
    #if defined (OSMac_)
        char fname[MAXPATHLEN];
        strcpy (fname, fileName.asChar());
        ofstream animFile(fname);
    #else
        ofstream animFile(fileName.asChar());
    #endif
        //  Defaults.
        //
        MString copyFlags("copyKey -cb api -fea 1 ");
        int precision = kDefaultPrecision;
        bool nodeNames = true;
        bool verboseUnits = false;

        //  Parse the options. The options syntax is in the form of
        //  "flag=val;flag1=val;flag2=val"
        //
        MString exportFlags;
        if (options.length() > 0) {
            const MString flagPrecision("precision");
            const MString flagNodeNames("nodeNames");
            const MString flagVerboseUnits("verboseUnits");
            const MString flagCopyKeyCmd("copyKeyCmd");

            //  Start parsing.
            //
            MStringArray optionList;
            MStringArray theOption;
            options.split(';', optionList);

            unsigned nOptions = optionList.length();
            for (unsigned i = 0; i < nOptions; i++) {
                theOption.clear();
                optionList[i].split('=', theOption);
                if (theOption.length() < 1) {
                    continue;
                }

                if (theOption[0] == flagPrecision && theOption.length() > 1) {
                    if (theOption[1].isInt()) {
                        precision = theOption[1].asInt();
                    }
                } else if ( theOption[0] ==
                            flagNodeNames && theOption.length() > 1) {
                    if (theOption[1].isInt()) {
                        nodeNames = (theOption[1].asInt()) ? true : false;
                    }
                }
                else if (   theOption[0] ==
                        flagVerboseUnits && theOption.length() > 1) {
                    if (theOption[1].isInt()) {
                        verboseUnits = (theOption[1].asInt()) ? true : false;
                    }
                } else if ( theOption[0] ==
                            flagCopyKeyCmd && theOption.length() > 1) {

                    //  Replace any '>' characters with '"'. This is needed
                    //  since the file translator option boxes do not handle
                    //  escaped quotation marks.
                    //
                    const char *optStr = theOption[1].asChar();
                    size_t nChars = strlen(optStr);
                    char *copyStr = new char[nChars+1];

                    copyStr = strcpy(copyStr, optStr);
                    for (size_t j = 0; j < nChars; j++) {
                        if (copyStr[j] == '>') {
                            copyStr[j] = '"';
                        }
                    }

                    copyFlags += copyStr;
                    delete [] copyStr;
                }
            }
        }

        //  Set the precision of the ofstream.
        //
        animFile.precision(precision);

        status = exportSelected(animFile, copyFlags, nodeNames, verboseUnits);

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

        return status;
    }

    bool animExport::haveReadMethod() const
    {
        return false;
    }

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

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

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

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

        return  kNotMyFileType;
    }

    MStatus animExport::exportSelected( ofstream &animFile,
                                        MString &copyFlags,
                                        bool nodeNames /* false */,
                                        bool verboseUnits /* false */)
    {
        MStatus status = MS::kFailure;

        //  If the selection list is empty, then there are no anim curves
        //  to export.
        //
        MSelectionList sList;
        MGlobal::getActiveSelectionList(sList);
        if (sList.isEmpty()) {
            MString msg = MStringResource::getString(kNothingSelected, status);
            MGlobal::displayError(msg);
            return (MS::kFailure);
        }

        //  Copy any anim curves to the API clipboard.
        //
        int result = 0;
        MString command(copyFlags);

        if (MS::kSuccess != (status =
            MGlobal::executeCommand(command, result, false, true))) {
            MStatus stringStat;
            MString msg = MStringResource::getString(kAnimCurveNotFound, stringStat);
            MGlobal::displayError(msg);
            return status;
                }

        if (result == 0 || MAnimCurveClipboard::theAPIClipboard().isEmpty()) {
            MString msg = MStringResource::getString(kAnimCurveNotFound, status);
            MGlobal::displayError(msg);
            return (MS::kFailure);
        }

        if (MS::kSuccess != (   status =
                                fWriter.writeClipboard(animFile,
                                MAnimCurveClipboard::theAPIClipboard(),
                                nodeNames, verboseUnits))) {
            return (MS::kFailure);
        }

        return status;
    }

    MStatus initializePlugin(MObject obj)
    {
        MStatus stat = MS::kFailure;
        MFnPlugin impPlugIn(obj, PLUGIN_COMPANY, "3.0", "Any");

        // This is done first, so the strings are available.
        stat = impPlugIn.registerUIStrings(registerMStringResources, "animImportExportInitStrings");
        if (stat != MS::kSuccess)
        {
            stat.perror("registerUIStrings");
            return stat;
        }

        stat = impPlugIn.registerFileTranslator("animImport", "none",
                                                animImport::creator,
                                                (char *)animImportOptionScript,
                                                (char *)animImportDefaultOptions,
                                                true);

        if (stat != MS::kSuccess) {
            return stat;
        }

        MFnPlugin expPlugIn(obj, PLUGIN_COMPANY, "3.0", "Any");
        stat = expPlugIn.registerFileTranslator("animExport", "",
                                            animExport::creator,
                                            (char *)animExportOptionScript,
                                            (char *)animExportDefaultOptions,
                                            true);

        return stat;
    }

    MStatus uninitializePlugin(MObject obj)
    {
        MStatus stat = MS::kFailure;

        MFnPlugin impPlugIn(obj);
        stat = impPlugIn.deregisterFileTranslator("animImport");

        if (stat != MS::kSuccess) {
            return stat;
        }

        MFnPlugin expPlugIn(obj);
        stat = expPlugIn.deregisterFileTranslator("animExport");

        return stat;
    }