lepTranslator.cpp

//-
// ==========================================================================
// Copyright 2010 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.
// ==========================================================================
//+

//
//  This example plugin demonstrates how to implement a Maya File Translator.
//  In this example, the user can create one or more nurbsSpheres, nurbsCylinders or nurbsCones.
//  The user can translate them around.
//
//  The LEP files can be referenced by Maya files.
//  
//  It is to be noted that this example was made to be simple.  Hence, there are limitations.
//  For example, every geometry saved will have its values reset to default,
//  except their translation if the option "Show Position" has been turned on.  To find what 
//  geometries we can export, we search by name, hence, if a polygon cube contains in its 
//  named the string "nurbsSphere", it will be written out as a nurbs sphere.
//

#include <maya/MStatus.h>
#include <maya/MObject.h>
#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MVector.h>
#include <maya/MStringArray.h>
#include <maya/MPxFileTranslator.h>
#include <maya/MGlobal.h>
#include <maya/MItDag.h>
#include <maya/MObject.h>
#include <maya/MPlug.h>
#include <maya/MItSelectionList.h>
#include <maya/MSelectionList.h>
#include <maya/MIOStream.h>
#include <maya/MFStream.h>
#include <maya/MFileIO.h>
#include <maya/MFnTransform.h>
#include <maya/MNamespace.h>
#include <string.h>


//This is the backbone for creating a MPxFileTranslator
class LepTranslator : public MPxFileTranslator {
public:

    //Constructor
    LepTranslator () {};
    //Destructor
    virtual            ~LepTranslator () {};

    //This tells maya that the translator can read files.
    //Basically, you can import or load with your translator.
    bool haveReadMethod() const { return true; }

    //This tells maya that the translator can write files.
    //Basically, you can export or save with your translator.
    bool haveWriteMethod() const { return true; }

    //If this method returns true, and the lep file is referenced in a scene, the write method will be
    //called when a write operation is performed on the parent file.  This use is for users who wish
    //to implement a custom file referencing system.
    //For this example, we will return false as we will use Maya's file referencing system.
    bool haveReferenceMethod() const { return false; }

    //If this method returns true, it means we support namespaces.
    bool haveNamespaceSupport()    const { return true; }

    //This method is used by Maya to create instances of the translator.
    static void* creator();
    
    //This returns the default extension ".lep" in this case.
    MString defaultExtension () const;

    //If this method returns true it means that the translator can handle opening files 
    //as well as importing them.
    //If the method returns false then only imports are handled. The difference between 
    //an open and an import is that the scene is cleared(e.g. 'file -new') prior to an 
    //open, which may affect the behaviour of the translator.
    bool canBeOpened() const { return true; }

    //Maya will call this method to determine if our translator
    //is capable of handling this file.
    MFileKind identifyFile (    const MFileObject& fileName,
                                                const char* buffer,
                                                short size) const;

    //This function is called by maya when import or open is called.
    MStatus reader ( const MFileObject& file,
                                        const MString& optionsString,
                            MPxFileTranslator::FileAccessMode mode);

    //This function is called by maya when export or save is called.
    MStatus writer ( const MFileObject& file,
                                        const MString& optionsString,
                             MPxFileTranslator::FileAccessMode mode);

private:
    //The magic string to verify it's a LEP file
    //simply "<LEP>"
    static MString const magic;
};

//Creates one instance of the LepTranslator
void* LepTranslator::creator()
{
    return new LepTranslator();
}

// Initialize our magic string
MString const LepTranslator::magic("<LEP>");

// An LEP file is an ascii whose first line contains the string <LEP>.
// The read does not support comments, and assumes that the each
// subsequent line of the file contains a valid MEL command that can
// be executed via the "executeCommand" method of the MGlobal class.
//
MStatus LepTranslator::reader ( const MFileObject& file,
                                const MString& options,
                                MPxFileTranslator::FileAccessMode mode)
{    
    #if defined (OSMac_)
        char nameBuffer[MAXPATHLEN];
        strcpy (nameBuffer, file.fullName().asChar());
        const MString fname(nameBuffer);
    #else
        const MString fname = file.fullName();
    #endif    

    MStatus rval(MS::kSuccess);
    const int maxLineSize = 1024;
    char buf[maxLineSize];

    ifstream inputfile(fname.asChar(), ios::in);
    if (!inputfile) {
        // open failed
        cerr << fname << ": could not be opened for reading\n";
        return MS::kFailure;
    }

    if (!inputfile.getline (buf, maxLineSize)) {
        cerr << "file " << fname << " contained no lines ... aborting\n";
        return MS::kFailure;
    }

    if (0 != strncmp(buf, magic.asChar(), magic.length())) {
        cerr << "first line of file " << fname;
        cerr << " did not contain " << magic.asChar() << " ... aborting\n";
        return MS::kFailure;
    }

    while (inputfile.getline (buf, maxLineSize)) {
        MString cmdString;

        cmdString.set(buf);
        if (!MGlobal::executeCommand(cmdString))
            rval = MS::kFailure;
    }
    inputfile.close();

    return rval;
}

// The currently recognised primitives.
const char* primitiveStrings[] = {
    "nurbsSphere",
    "nurbsCone",
    "nurbsCylinder",
};
const unsigned numPrimitives = sizeof(primitiveStrings) / sizeof(char*);

// Corresponding commands to create the primitives
const char* primitiveCommands[] = {
    "sphere",
    "cone",
    "cylinder",
};

//The writer simply goes gathers all objects from the scene.
//We will check if the object has a transform, if so, we will check
//if it's either a nurbsSphere, nurbsCone or nurbsCylinder.  If so,
//we will write it out.
MStatus LepTranslator::writer ( const MFileObject& file,
                                const MString& options,
                                MPxFileTranslator::FileAccessMode mode)
{
    MStatus status;
    bool showPositions = false;
    unsigned int  i;
    const MString fname = file.fullName();

    ofstream newf(fname.asChar(), ios::out);
    if (!newf) {
        // open failed
        cerr << fname << ": could not be opened for reading\n";
        return MS::kFailure;
    }
    newf.setf(ios::unitbuf);

    if (options.length() > 0) {
        // Start parsing.
        MStringArray optionList;
        MStringArray theOption;
        options.split(';', optionList);    // break out all the options.
        
        for( i = 0; i < optionList.length(); ++i ){
            theOption.clear();
            optionList[i].split( '=', theOption );
            if( theOption[0] == MString("showPositions") &&
                                                    theOption.length() > 1 ) {
                if( theOption[1].asInt() > 0 ){
                    showPositions = true;
                }else{
                    showPositions = false;
                }
            }
        }
    }

    // output our magic number
    newf << "<LEP>\n";

    MItDag dagIterator( MItDag::kBreadthFirst, MFn::kInvalid, &status);

    if ( !status) {
        status.perror ("Failure in DAG iterator setup");
        return MS::kFailure;
    }

    MSelectionList selection;
    MGlobal::getActiveSelectionList (selection);
    MItSelectionList selIterator (selection, MFn::kDagNode);

    bool done = false;
    while (true) 
    {
        MObject currentNode;
        switch (mode)
        {
            case MPxFileTranslator::kSaveAccessMode:
            case MPxFileTranslator::kExportAccessMode:
                if (dagIterator.isDone ())
                    done = true;
                else {
                    currentNode = dagIterator.item ();
                    dagIterator.next ();
                }
                break;
            case MPxFileTranslator::kExportActiveAccessMode:
                if (selIterator.isDone ())
                    done = true;
                else {
                    selIterator.getDependNode (currentNode);
                    selIterator.next ();
                }
                break;
            default:
                cerr << "Unrecognized write mode: " << mode << endl;
                break;
        }
        if (done)
            break;

        //We only care about nodes that are transforms
        MFnTransform dagNode(currentNode, &status);
        if ( status == MS::kSuccess ) 
        {
            MString nodeNameNoNamespace=MNamespace::stripNamespaceFromName(dagNode.name());
            for (i = 0; i < numPrimitives; ++i) {                
                if(nodeNameNoNamespace.indexW(primitiveStrings[i]) >= 0){
                    // This is a node we support
                    newf << primitiveCommands[i] << " -n " << nodeNameNoNamespace << endl;
                    if (showPositions) {
                        MVector pos;
                        pos = dagNode.getTranslation(MSpace::kObject);
                        newf << "move " << pos.x << " " << pos.y << " " << pos.z << endl;
                    }
                }
            }
        }//if (status == MS::kSuccess)
    }//while loop

    newf.close();
    return MS::kSuccess;
}

// Whenever Maya needs to know the preferred extension of this file format,
// it calls this method. For example, if the user tries to save a file called
// "test" using the Save As dialog, Maya will call this method and actually
// save it as "test.lep". Note that the period should *not* be included in
// the extension.
MString LepTranslator::defaultExtension () const
{
    return "lep";
}


//This method is pretty simple, maya will call this function
//to make sure it is really a file from our translator.
//To make sure, we have a little magic number and we verify against it.
MPxFileTranslator::MFileKind LepTranslator::identifyFile (
                                        const MFileObject& fileName,
                                        const char* buffer,
                                        short size) const
{
    // Check the buffer for the "LEP" magic number, the
    // string "<LEP>\n"

    MFileKind rval = kNotMyFileType;

    if ((size >= (short)magic.length()) &&
        (0 == strncmp(buffer, magic.asChar(), magic.length()))) 
    {
        rval = kIsMyFileType;
    }
    return rval;
}

MStatus initializePlugin( MObject obj )
{
    MStatus   status;
    MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

    // Register the translator with the system
    // The last boolean in this method is very important.
    // It should be set to true if the reader method in the derived class
    // intends to issue MEL commands via the MGlobal::executeCommand 
    // method.  Setting this to true will slow down the creation of
    // new objects, but allows MEL commands other than those that are
    // part of the Maya Ascii file format to function correctly.
    status =  plugin.registerFileTranslator( "Lep",
                                        "lepTranslator.rgb",
                                        LepTranslator::creator,
                                        "lepTranslatorOpts",
                                        "showPositions=1",
                                        true );
    if (!status) 
    {
        status.perror("registerFileTranslator");
        return status;
    }

    return status;
}

MStatus uninitializePlugin( MObject obj )
{
    MStatus   status;
    MFnPlugin plugin( obj );

    status =  plugin.deregisterFileTranslator( "Lep" );
    if (!status)
    {
        status.perror("deregisterFileTranslator");
        return status;
    }

    return status;
}