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

# objExport.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.
    //
    // ==========================================================================
    //+

    #include <string.h>
    #include <sys/types.h>
    #include <maya/MStatus.h>
    #include <maya/MPxCommand.h>
    #include <maya/MString.h>
    #include <maya/MStringArray.h>
    #include <maya/MArgList.h>
    #include <maya/MGlobal.h>
    #include <maya/MSelectionList.h>
    #include <maya/MItSelectionList.h>
    #include <maya/MPoint.h>
    #include <maya/MPointArray.h>
    #include <maya/MDagPath.h>
    #include <maya/MDagPathArray.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MFnMesh.h>
    #include <maya/MFnSet.h>
    #include <maya/MItMeshPolygon.h>
    #include <maya/MItMeshVertex.h>
    #include <maya/MItMeshEdge.h>
    #include <maya/MFloatVector.h>
    #include <maya/MFloatVectorArray.h>
    #include <maya/MFloatArray.h>
    #include <maya/MObjectArray.h>
    #include <maya/MObject.h>
    #include <maya/MPlug.h>
    #include <maya/MPxFileTranslator.h>
    #include <maya/MFnDagNode.h>
    #include <maya/MItDag.h>
    #include <maya/MDistance.h>
    #include <maya/MIntArray.h>
    #include <maya/MIOStream.h>

    #if defined (_WIN32)
    #define strcasecmp stricmp
    #elif defined  (OSMac_)
    extern "C" int strcasecmp (const char *, const char *);
    extern "C" Boolean createMacFile (const char *fileName, FSRef *fsRef, long creator, long type);
    #endif

    #define NO_SMOOTHING_GROUP      -1
    #define INITIALIZE_SMOOTHING    -2
    #define INVALID_ID              -1

    //
    // Edge info structure
    //
    typedef struct EdgeInfo {
        int                 polyIds[2]; // Id's of polygons that reference edge
        int                 vertId;     // The second vertex of this edge
        struct EdgeInfo *   next;       // Pointer to next edge
        bool                smooth;     // Is this edge smooth
    } * EdgeInfoPtr;

    class ObjTranslator : public MPxFileTranslator {
    public:
                        ObjTranslator () {};
        virtual         ~ObjTranslator () {};
        static void*    creator();

        MStatus         reader ( const MFileObject& file,
                                 const MString& optionsString,
                                 FileAccessMode mode);

        MStatus         writer ( const MFileObject& file,
                                 const MString& optionsString,
                                 FileAccessMode mode );
        bool            haveReadMethod () const;
        bool            haveWriteMethod () const;
        MString         defaultExtension () const;
        MFileKind       identifyFile ( const MFileObject& fileName,
                                       const char* buffer,
                                       short size) const;
    private:
        void            outputSetsAndGroups    ( MDagPath&, int, bool, int );
        MStatus         OutputPolygons( MDagPath&, MObject& );
        MStatus         exportSelected();
        MStatus         exportAll();
        void            initializeSetsAndLookupTables( bool exportAll );
        void            freeLookupTables();
        bool            lookup( MDagPath&, int, int, bool );
        void            setToLongUnitName( const MDistance::Unit&, MString& );
        void            recFindTransformDAGNodes( MString&, MIntArray& );

        // Edge lookup methods
        //
        void            buildEdgeTable( MDagPath& );
        void            addEdgeInfo( int, int, bool );
        EdgeInfoPtr     findEdgeInfo( int, int );
        void            destroyEdgeTable();
        bool            smoothingAlgorithm( int, MFnMesh& );

    private:
        // counters
        int v,vt,vn;
        // offsets
        int voff,vtoff,vnoff;
        // options
        bool groups, ptgroups, materials, smoothing, normals;

        FILE *fp;

        // Keeps track of all sets.
        //
        int numSets;
        MObjectArray *sets;

        // Keeps track of all objects and components.
        // The Tables are used to mark which sets each
        // component belongs to.
        //
        MStringArray *objectNames;

        bool **polygonTablePtr;
        bool **vertexTablePtr;
        bool * polygonTable;
        bool * vertexTable;
        bool **objectGroupsTablePtr;

        // Used to determine if the last set(s) written out are the same
        // as the current sets to be written. We don't need to write out
        // sets unless they change between components. Same goes for
        // materials.
        //
        MIntArray *lastSets;
        MIntArray *lastMaterials;

        // We have to do 2 dag iterations so keep track of the
        // objects found in the first iteration by this index.
        //
        int objectId;
        int objectCount;

        // Edge lookup table (by vertex id) and smoothing group info
        //
        EdgeInfoPtr *   edgeTable;
        int *           polySmoothingGroups;
        int             edgeTableSize;
        int             nextSmoothingGroup;
        int             currSmoothingGroup;
        bool            newSmoothingGroup;

        // List of names of the mesh shapes that we export from maya
        MStringArray    objectNodeNamesArray;

        // Used to keep track of Maya groups (transform DAG nodes) that
        // contain objects being exported
        MStringArray    transformNodeNameArray;
    };

    const char *const objOptionScript = "objExportOptions";
    const char *const objDefaultOptions =
        "groups=1;"
        "ptgroups=1;"
        "materials=1;"
        "smoothing=1;"
        "normals=1;"
        ;

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

    MStatus ObjTranslator::reader ( const MFileObject& file,
                                    const MString& options,
                                    FileAccessMode mode)
    {
        fprintf(stderr, "ObjTranslator::reader called in error\n");
        return MS::kFailure;
    }

    #if defined (OSMac_)

    // Convert file system representations
    // Possible styles: kCFURLHFSPathStyle, kCFURLPOSIXPathStyle
    // kCFURLHFSPathStyle = Emerald:aw:Maya:projects:default:scenes:eagle.ma
    // kCFURLPOSIXPathStyle = /Volumes/Emerald/aw/Maya/projects/default/scenes/eagle.ma
    // The conversion will be done in place, so make sure fileName is big enough
    // to hold the result
    //
    static Boolean
    convertFileRepresentation (char *fileName, short inStyle, short outStyle)
    {
        if (fileName == NULL) {
            return (false);
        }
        if (inStyle == outStyle) {
            return (true);
        }

        CFStringRef rawPath = CFStringCreateWithCString (NULL, fileName, kCFStringEncodingUTF8);
        if (rawPath == NULL) {
            return (false);
        }
        CFURLRef baseURL = CFURLCreateWithFileSystemPath (NULL, rawPath, (CFURLPathStyle)inStyle, false);
        CFRelease (rawPath);
        if (baseURL == NULL) {
            return (false);
        }
        CFStringRef newURL = CFURLCopyFileSystemPath (baseURL, (CFURLPathStyle)outStyle);
        CFRelease (baseURL);
        if (newURL == NULL) {
            return (false);
        }
        char newPath[MAXPATHLEN];
        CFStringGetCString (newURL, newPath, MAXPATHLEN, kCFStringEncodingUTF8);
        CFRelease (newURL);
        strcpy (fileName, newPath);
        return (true);
    }

    #endif

    MStatus ObjTranslator::writer ( const MFileObject& file,
                                    const MString& options,
                                    FileAccessMode mode )

    {
        MStatus status;

        MString mname = file.fullName(), unitName;

    #if defined (OSMac_)
        char fname[MAXPATHLEN];
        strcpy (fname, file.fullName().asChar());
        FSRef notUsed;
        //Create a file else convertFileRep will fail.
        createMacFile (fname, &notUsed, 0, 0);
        convertFileRepresentation (fname, kCFURLPOSIXPathStyle, kCFURLHFSPathStyle);
        fp = fopen(fname,"wb");//MAYAMACTODO
    #else
        const char *fname = mname.asChar();
        fp = fopen(fname,"w");
    #endif

        if (fp == NULL)
        {
            cerr << "Error: The file " << fname << " could not be opened for writing." << endl;
            return MS::kFailure;
        }

        // Options
        //
        groups      = true; // write out facet groups
        ptgroups    = true; // write out vertex groups
        materials   = true; // write out shading groups
        smoothing   = true; // write out facet smoothing information
        normals     = true; // write out normal table and facet normals

      if (options.length() > 0) {
            int i, length;
            // Start parsing.
            MStringArray optionList;
            MStringArray theOption;
            options.split(';', optionList); // break out all the options.

            length = optionList.length();
            for( i = 0; i < length; ++i ){
                theOption.clear();
                optionList[i].split( '=', theOption );
                if( theOption[0] == MString("groups") &&
                                                        theOption.length() > 1 ) {
                    if( theOption[1].asInt() > 0 ){
                        groups = true;
                    }else{
                        groups = false;
                    }
                }
                if( theOption[0] == MString("materials") &&
                                                        theOption.length() > 1 ) {
                    if( theOption[1].asInt() > 0 ){
                        materials = true;
                    }else{
                        materials = false;
                    }
                }
                if( theOption[0] == MString("ptgroups") &&
                                                        theOption.length() > 1 ) {
                    if( theOption[1].asInt() > 0 ){
                        ptgroups = true;
                    }else{
                        ptgroups = false;
                    }
                }
                if( theOption[0] == MString("normals") &&
                                                        theOption.length() > 1 ) {
                    if( theOption[1].asInt() > 0 ){
                        normals = true;
                    }else{
                        normals = false;
                    }
                }
                if( theOption[0] == MString("smoothing") &&
                                                        theOption.length() > 1 ) {
                    if( theOption[1].asInt() > 0 ){
                        smoothing = true;
                    }else{
                        smoothing = false;
                    }
                }
            }
        }

        /* print current linear units used as a comment in the obj file */
        setToLongUnitName(MDistance::uiUnit(), unitName);
        //fprintf( fp, "# This file uses %s as units for non-parametric coordinates.\n\n", unitName.asChar() );
        fprintf( fp, "# The units used in this file are %s.\n", unitName.asChar() );

        if( ( mode == MPxFileTranslator::kExportAccessMode ) ||
            ( mode == MPxFileTranslator::kSaveAccessMode ) )
        {
            exportAll();
        }
        else if( mode == MPxFileTranslator::kExportActiveAccessMode )
        {
            exportSelected();
        }
        fclose(fp);

        return MS::kSuccess;
    }

    void ObjTranslator::setToLongUnitName(const MDistance::Unit &unit, MString& unitName)
    {
        switch( unit )
        {
        case MDistance::kInches:
            unitName = "inches";
            break;
        case MDistance::kFeet:
            unitName = "feet";
            break;
        case MDistance::kYards:
            unitName = "yards";
            break;
        case MDistance::kMiles:
            unitName = "miles";
            break;
        case MDistance::kMillimeters:
            unitName = "millimeters";
            break;
        case MDistance::kCentimeters:
            unitName = "centimeters";
            break;
        case MDistance::kKilometers:
            unitName = "kilometers";
            break;
        case MDistance::kMeters:
            unitName = "meters";
            break;
        default:
            break;
        }
    }

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

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

    MString ObjTranslator::defaultExtension () const
    {
        return "obj";
    }

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

        if ((nameLength > 4) && !strcasecmp(name+nameLength-4, ".obj"))
            return kCouldBeMyFileType;
        else
            return kNotMyFileType;
    }

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

        // Register the translator with the system
        return plugin.registerFileTranslator( "OBJexport", "none",
                                              ObjTranslator::creator,
                                              (char *)objOptionScript,
                                              (char *)objDefaultOptions );
    }

    MStatus uninitializePlugin( MObject obj )
    {
            MFnPlugin plugin( obj );
            return plugin.deregisterFileTranslator( "OBJexport" );
    }

    MStatus ObjTranslator::OutputPolygons(
            MDagPath& mdagPath,
            MObject&  mComponent
    )
    {
        MStatus stat = MS::kSuccess;
        MSpace::Space space = MSpace::kWorld;
        int i;

        MFnMesh fnMesh( mdagPath, &stat );
        if ( MS::kSuccess != stat) {
            fprintf(stderr,"Failure in MFnMesh initialization.\n");
            return MS::kFailure;
        }

        MItMeshPolygon polyIter( mdagPath, mComponent, &stat );
        if ( MS::kSuccess != stat) {
            fprintf(stderr,"Failure in MItMeshPolygon initialization.\n");
            return MS::kFailure;
        }
        MItMeshVertex vtxIter( mdagPath, mComponent, &stat );
        if ( MS::kSuccess != stat) {
            fprintf(stderr,"Failure in MItMeshVertex initialization.\n");
            return MS::kFailure;
        }

        int objectIdx = -1, length;
        MString mdagPathNodeName = fnMesh.name();
        // Find i such that objectGroupsTablePtr[i] corresponds to the
        // object node pointed to by mdagPath
        length = objectNodeNamesArray.length();
        for( i=0; i<length; i++ ) {
            if( objectNodeNamesArray[i] == mdagPathNodeName ) {
                objectIdx = i;
                break;
            }
        }

        // Write out the vertex table
        //

        for ( ; !vtxIter.isDone(); vtxIter.next() ) {
            MPoint p = vtxIter.position( space );
            if (ptgroups && groups && (objectIdx >= 0)) {
                int compIdx = vtxIter.index();
                outputSetsAndGroups( mdagPath, compIdx, true, objectIdx );
            }
            // convert from internal units to the current ui units
            p.x = MDistance::internalToUI(p.x);
            p.y = MDistance::internalToUI(p.y);
            p.z = MDistance::internalToUI(p.z);
            fprintf(fp,"v %f %f %f\n",p.x,p.y,p.z);
            v++;
        }

        // Write out the uv table
        //
        MFloatArray uArray, vArray;
        fnMesh.getUVs( uArray, vArray );
        int uvLength = uArray.length();
        for ( int x=0; x<uvLength; x++ ) {
            fprintf(fp,"vt %f %f\n",uArray[x],vArray[x]);
            vt++;
        }

        // Write out the normal table
        //
        if ( normals ) {
            MFloatVectorArray norms;
            fnMesh.getNormals( norms, MSpace::kWorld );
            int normsLength = norms.length();
            for ( int t=0; t<normsLength; t++ ) {
                MFloatVector tmpf = norms[t];
                fprintf(fp,"vn %f %f %f\n",tmpf[0],tmpf[1],tmpf[2]);
                vn++;
            }
        }

        // For each polygon, write out:
        //    s  smoothing_group
        //    sets/groups the polygon belongs to
        //    f  vertex_index/uvIndex/normalIndex
        //
        int lastSmoothingGroup = INITIALIZE_SMOOTHING;

        for ( ; !polyIter.isDone(); polyIter.next() )
        {
            // Write out the smoothing group that this polygon belongs to
            // We only write out the smoothing group if it is different
            // from the last polygon.
            //
            if ( smoothing ) {
                int compIdx = polyIter.index();
                int smoothingGroup = polySmoothingGroups[ compIdx ];

                if ( lastSmoothingGroup != smoothingGroup ) {
                    if ( NO_SMOOTHING_GROUP == smoothingGroup ) {
                        fprintf(fp,"s off\n");
                    }
                    else {
                        fprintf(fp,"s %d\n", smoothingGroup );
                    }
                    lastSmoothingGroup = smoothingGroup;
                }
            }

            // Write out all the sets that this polygon belongs to
            //
            if ((groups || materials) && (objectIdx >= 0)) {
                int compIdx = polyIter.index();
                outputSetsAndGroups( mdagPath, compIdx, false, objectIdx );
            }

            // Write out vertex/uv/normal index information
            //
            fprintf(fp,"f");
            int polyVertexCount = polyIter.polygonVertexCount();
            for ( int vtx=0; vtx<polyVertexCount; vtx++ ) {
                fprintf(fp," %d", polyIter.vertexIndex( vtx ) +1 +voff);

                bool noUV = true;
                if ( fnMesh.numUVs() > 0 ) {
                    int uvIndex;
                    // If the call to getUVIndex fails then there is no
                    // mapping information for this polyon so we don't write
                    // anything
                    //
                    if ( polyIter.getUVIndex(vtx,uvIndex) ) {
                        fprintf(fp,"/%d",uvIndex+1 +vtoff);
                        noUV = false;
                    }
                }

                if ( (normals) && (fnMesh.numNormals() > 0) ) {
                    if ( noUV ) {
                        // If there are no UVs then our polygon is written
                        // in the form vertex//normal
                        //
                        fprintf(fp,"/");
                    }
                    fprintf(fp,"/%d",polyIter.normalIndex( vtx ) +1 +vnoff);
                }
            }
            fprintf(fp,"\n");
            fflush(fp);
        }
        return stat;
    }

    void ObjTranslator::outputSetsAndGroups(
        MDagPath & mdagPath,
        int cid,
        bool isVertexIterator,
        int objectIdx

    )
    {
        MStatus stat;

        int i, length;
        MIntArray * currentSets = new MIntArray;
        MIntArray * currentMaterials = new MIntArray;
        MStringArray gArray, mArray;

        if (groups || materials) {

            for ( i=0; i<numSets; i++ )
            {
                if ( lookup(mdagPath,i,cid,isVertexIterator) ) {

                    MFnSet fnSet( (*sets)[i] );
                    if ( MFnSet::kRenderableOnly == fnSet.restriction(&stat) ) {
                        currentMaterials->append( i );
                        mArray.append( fnSet.name() );
                    }
                    else {
                        currentSets->append( i );
                        gArray.append( fnSet.name() );
                    }
                }
            }

            if( !isVertexIterator ) {
                // export group nodes (transform DAG nodes) in Maya that
                // the current object is a
                // child/grandchild/grandgrandchild/... of
                bool *objectGroupTable = objectGroupsTablePtr[objectIdx];
                length = transformNodeNameArray.length();
                for( i=0; i<length; i++ ) {
                    if( objectGroupTable[i] ) {
                        currentSets->append( numSets + i );
                        gArray.append(transformNodeNameArray[i]);
                    }
                }
            }

            // prevent grouping incoherence, use tav default group schema.
            //
            if (0 == currentSets->length())
            {
                currentSets->append( 0 );
                gArray.append( "default" );
            }

            // Test for equivalent sets
            //
            bool setsEqual = false;
            if ( (lastSets != NULL) &&
                  (lastSets->length() == currentSets->length())
            ) {
                setsEqual = true;
                length = lastSets->length();
                for ( i=0; i<length; i++ )
                {
                    if ( (*lastSets)[i] != (*currentSets)[i] ) {
                        setsEqual = false;
                        break;
                    }
                }
            }

            if ( !setsEqual ) {
                if ( lastSets != NULL )
                    delete lastSets;

                lastSets = currentSets;

                if (groups) {
                    int gLength = gArray.length();
                    if ( gLength > 0  ) {
                        fprintf(fp,"g");
                        for ( i=0; i<gLength; i++ ) {
                            fprintf(fp," %s",gArray[i].asChar());
                        }
                        fprintf(fp,"\n");
                    }
                }
            }
            else
            {
                delete currentSets;
            }

            // Test for equivalent materials
            //
            bool materialsEqual = false;
            if ( (lastMaterials != NULL) &&
                  (lastMaterials->length() == currentMaterials->length())
            ) {
                materialsEqual = true;
                length = lastMaterials->length();
                for ( i=0; i<length; i++ )
                {
                    if ( (*lastMaterials)[i] != (*currentMaterials)[i] ) {
                        materialsEqual = false;
                        break;
                    }
                }
            }

            if ( !materialsEqual ) {
                if ( lastMaterials != NULL )
                    delete lastMaterials;

                lastMaterials = currentMaterials;

                if (materials) {

                    int mLength = mArray.length();

                    if ( mLength > 0  ) {
                        fprintf(fp,"usemtl");
                        for ( i=0; i<mLength; i++ ) {
                            fprintf(fp," %s",mArray[i].asChar());
                        }
                        fprintf(fp,"\n");
                    }
                }
            }
            else
            {
                delete currentMaterials;
            }
        }
    }

    void ObjTranslator::initializeSetsAndLookupTables( bool exportAll )
    //
    // Description :
    //    Creates a list of all sets in Maya, a list of mesh objects,
    //    and polygon/vertex lookup tables that will be used to
    //    determine which sets are referenced by the poly components.
    //
    {
        int i=0,j=0, length;
        MStatus stat;

        // Initialize class data.
        // Note: we cannot do this in the constructor as it
        // only gets called upon registry of the plug-in.
        //
        numSets = 0;
        sets = NULL;
        lastSets = NULL;
        lastMaterials = NULL;
        objectId = 0;
        objectCount = 0;
        polygonTable = NULL;
        vertexTable = NULL;
        polygonTablePtr = NULL;
        vertexTablePtr = NULL;
        objectGroupsTablePtr = NULL;
        objectNodeNamesArray.clear();
        transformNodeNameArray.clear();

        //
        // Find all sets in Maya and store the ones we care about in
        // the 'sets' array. Also make note of the number of sets.
        //

        // Get all of the sets in maya and put them into
        // a selection list
        //
        MStringArray result;
        MGlobal::executeCommand( "ls -sets", result );
        MSelectionList * setList = new MSelectionList;
        length = result.length();
        for ( i=0; i<length; i++ )
        {
            setList->add( result[i] );
        }

        // Extract each set as an MObject and add them to the
        // sets array.
        // We may be excluding groups, matierials, or ptGroups
        // in which case we can ignore those sets.
        //
        MObject mset;
        sets = new MObjectArray();
        length = setList->length();
        for ( i=0; i<length; i++ )
        {
            setList->getDependNode( i, mset );

            MFnSet fnSet( mset, &stat );
            if ( stat ) {
                if ( MFnSet::kRenderableOnly == fnSet.restriction(&stat) ) {
                    if ( materials ) {
                        sets->append( mset );
                    }
                }
                else {
                    if ( groups ) {
                        sets->append( mset );
                    }
                }
            }
        }
        delete setList;

        numSets = sets->length();

        //
        // Do a dag-iteration and for every mesh found, create facet and
        // vertex look-up tables. These tables will keep track of which
        // sets each component belongs to.
        //
        // If exportAll is false then iterate over the activeSelection
        // list instead of the entire DAG.
        //
        // These arrays have a corrisponding entry in the name
        // stringArray.
        //
        MIntArray vertexCounts;
        MIntArray polygonCounts;

        if ( exportAll ) {
            MItDag dagIterator( MItDag::kBreadthFirst, MFn::kInvalid, &stat);

            if ( MS::kSuccess != stat) {
                fprintf(stderr,"Failure in DAG iterator setup.\n");
                return;
            }

            objectNames = new MStringArray;

            for ( ; !dagIterator.isDone(); dagIterator.next() )
            {
                MDagPath dagPath;
                stat = dagIterator.getPath( dagPath );

                if ( stat )
                {
                    // skip over intermediate objects
                    //
                    MFnDagNode dagNode( dagPath, &stat );
                    if (dagNode.isIntermediateObject())
                    {
                        continue;
                    }

                    if (( dagPath.hasFn(MFn::kMesh)) &&
                        ( dagPath.hasFn(MFn::kTransform)))
                    {
                        // We want only the shape,
                        // not the transform-extended-to-shape.
                        continue;
                    }
                    else if ( dagPath.hasFn(MFn::kMesh))
                    {
                        // We have a mesh so create a vertex and polygon table
                        // for this object.
                        //
                        MFnMesh fnMesh( dagPath );
                        int vtxCount = fnMesh.numVertices();
                        int polygonCount = fnMesh.numPolygons();
                        // we do not need this call anymore, we have the shape.
                        // dagPath.extendToShape();
                        MString name = dagPath.fullPathName();
                        objectNames->append( name );
                        objectNodeNamesArray.append( fnMesh.name() );

                        vertexCounts.append( vtxCount );
                        polygonCounts.append( polygonCount );

                        objectCount++;
                    }
                }
            }
        }
        else
        {
            MSelectionList slist;
            MGlobal::getActiveSelectionList( slist );
            MItSelectionList iter( slist );
            MStatus status;

            objectNames = new MStringArray;

            // We will need to interate over a selected node's heirarchy
            // in the case where shapes are grouped, and the group is selected.
            MItDag dagIterator( MItDag::kDepthFirst, MFn::kInvalid, &status);

            for ( ; !iter.isDone(); iter.next() )
            {
                MDagPath objectPath;
                stat = iter.getDagPath( objectPath );

                // reset iterator's root node to be the selected node.
                status = dagIterator.reset (objectPath.node(),
                                            MItDag::kDepthFirst, MFn::kInvalid );

                // DAG iteration beginning at at selected node
                for ( ; !dagIterator.isDone(); dagIterator.next() )
                {
                    MDagPath dagPath;
                    MObject  component = MObject::kNullObj;
                    status = dagIterator.getPath(dagPath);

                    if (!status) {
                        fprintf(stderr,"Failure getting DAG path.\n");
                        freeLookupTables();
                        return ;
                    }

                    // skip over intermediate objects
                    //
                    MFnDagNode dagNode( dagPath, &stat );
                    if (dagNode.isIntermediateObject())
                    {
                        continue;
                    }

                    if (( dagPath.hasFn(MFn::kMesh)) &&
                        ( dagPath.hasFn(MFn::kTransform)))
                    {
                        // We want only the shape,
                        // not the transform-extended-to-shape.
                        continue;
                    }
                    else if ( dagPath.hasFn(MFn::kMesh))
                    {
                        // We have a mesh so create a vertex and polygon table
                        // for this object.
                        //
                        MFnMesh fnMesh( dagPath );
                        int vtxCount = fnMesh.numVertices();
                        int polygonCount = fnMesh.numPolygons();

                        // we do not need this call anymore, we have the shape.
                        // dagPath.extendToShape();
                        MString name = dagPath.fullPathName();
                        objectNames->append( name );
                        objectNodeNamesArray.append( fnMesh.name() );

                        vertexCounts.append( vtxCount );
                        polygonCounts.append( polygonCount );

                        objectCount++;
                    }
                }
            }
        }

        // Now we know how many objects we are dealing with
        // and we have counts of the vertices/polygons for each
        // object so create the maya group look-up table.
        //
        if( objectCount > 0 ) {

            // To export Maya groups we traverse the hierarchy starting at
            // each objectNodeNamesArray[i] going towards the root collecting transform
            // nodes as we go.
            length = objectNodeNamesArray.length();
            for( i=0; i<length; i++ ) {
                MIntArray transformNodeNameIndicesArray;
                recFindTransformDAGNodes( objectNodeNamesArray[i], transformNodeNameIndicesArray );
            }

            if( transformNodeNameArray.length() > 0 ) {
                objectGroupsTablePtr = (bool**) malloc( sizeof(bool*)*objectCount );
                length = transformNodeNameArray.length();
                for ( i=0; i<objectCount; i++ )
                {
                    objectGroupsTablePtr[i] =
                        (bool*)calloc( length, sizeof(bool) );

                    if ( objectGroupsTablePtr[i] == NULL ) {
                        cerr << "Error: calloc returned NULL (objectGroupsTablePtr)\n";
                        return;
                    }
                }
            }
        }

        // Create the vertex/polygon look-up tables.
        //
        if ( objectCount > 0 ) {

            vertexTablePtr = (bool**) malloc( sizeof(bool*)*objectCount );
            polygonTablePtr = (bool**) malloc( sizeof(bool*)*objectCount );

            for ( i=0; i<objectCount; i++ )
            {
                vertexTablePtr[i] =
                     (bool*)calloc( vertexCounts[i]*numSets, sizeof(bool) );

                if ( vertexTablePtr[i] == NULL ) {
                    cerr << "Error: calloc returned NULL (vertexTable)\n";
                    return;
                }

                polygonTablePtr[i] =
                     (bool*)calloc( polygonCounts[i]*numSets, sizeof(bool) );
                if ( polygonTablePtr[i] == NULL ) {
                    cerr << "Error: calloc returned NULL (polygonTable)\n";
                    return;
                }
            }
        }

        // If we found no meshes then return
        //
        if ( objectCount == 0 ) {
            return;
        }

        //
        // Go through all of the set members (flattened lists) and mark
        // in the lookup-tables, the sets that each mesh component belongs
        // to.
        //
        //
        bool flattenedList = true;
        MDagPath object;
        MObject component;
        MSelectionList memberList;

        for ( i=0; i<numSets; i++ )
        {
            MFnSet fnSet( (*sets)[i] );
            memberList.clear();
            stat = fnSet.getMembers( memberList, flattenedList );

            if (MS::kSuccess != stat) {
                fprintf(stderr,"Error in fnSet.getMembers()!\n");
            }

            int m, numMembers;
            numMembers = memberList.length();
            for ( m=0; m<numMembers; m++ )
            {
                if ( memberList.getDagPath(m,object,component) ) {

                    if ( (!component.isNull()) && (object.apiType() == MFn::kMesh) )
                    {
                        if (component.apiType() == MFn::kMeshVertComponent) {
                            MItMeshVertex viter( object, component );
                            for ( ; !viter.isDone(); viter.next() )
                            {
                                int compIdx = viter.index();
                                MString name = object.fullPathName();

                                // Figure out which object vertexTable
                                // to get.
                                //

                                int o, numObjectNames;
                                numObjectNames = objectNames->length();
                                for ( o=0; o<numObjectNames; o++ ) {
                                    if ( (*objectNames)[o] == name ) {
                                        // Mark set i as true in the table
                                        //
                                        vertexTable = vertexTablePtr[o];
                                        *(vertexTable + numSets*compIdx + i) = true;
                                        break;
                                    }
                                }
                            }
                        }
                        else if (component.apiType() == MFn::kMeshPolygonComponent)
                        {
                            MItMeshPolygon piter( object, component );
                            for ( ; !piter.isDone(); piter.next() )
                            {
                                int compIdx = piter.index();
                                MString name = object.fullPathName();

                                // Figure out which object polygonTable
                                // to get.
                                //
                                int o, numObjectNames;
                                numObjectNames = objectNames->length();
                                for ( o=0; o<numObjectNames; o++ ) {
                                    if ( (*objectNames)[o] == name ) {

                                        // Mark set i as true in the table
                                        //

    // Check for bad components in the set
    //
    if ( compIdx >= polygonCounts[o] ) {
        cerr << "Error: component in set >= numPolygons, skipping!\n";
        cerr << "  Component index    = " << compIdx << endl;
        cerr << "  Number of polygons = " << polygonCounts[o] << endl;
        break;
    }

                                        polygonTable = polygonTablePtr[o];
                                        *(polygonTable + numSets*compIdx + i) = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else {

                    // There are no components, therefore we can mark
                    // all polygons as members of the given set.
                    //

                    if (object.hasFn(MFn::kMesh)) {

                        MFnMesh fnMesh( object, &stat );
                        if ( MS::kSuccess != stat) {
                            fprintf(stderr,"Failure in MFnMesh initialization.\n");
                            return;
                        }

                        // We are going to iterate over all the polygons.
                        //
                        MItMeshPolygon piter( object, MObject::kNullObj, &stat );
                        if ( MS::kSuccess != stat) {
                            fprintf(stderr,
                                    "Failure in MItMeshPolygon initialization.\n");
                            return;
                        }
                        for ( ; !piter.isDone(); piter.next() )
                        {
                            int compIdx = piter.index();
                            MString name = object.fullPathName();

                            // Figure out which object polygonTable to get.
                            //
                            int o, numObjectNames;
                            numObjectNames = objectNames->length();
                            for ( o=0; o<numObjectNames; o++ ) {
                                if ( (*objectNames)[o] == name ) {

    // Check for bad components in the set
    //
    if ( compIdx >= polygonCounts[o] ) {
        cerr << "Error: component in set >= numPolygons, skipping!\n";
        cerr << "  Component index    = " << compIdx << endl;
        cerr << "  Number of polygons = " << polygonCounts[o] << endl;
        break;
    }
                                    // Mark set i as true in the table
                                    //
                                    polygonTable = polygonTablePtr[o];
                                    *(polygonTable + numSets*compIdx + i) = true;
                                    break;
                                }
                            }
                        } // end of piter.next() loop
                    } // end of condition if (object.hasFn(MFn::kMesh))
                    } // end of else condifion if (!component.isNull())
                } // end of memberList.getDagPath(m,object,component)
            } // end of memberList loop
        } // end of for-loop for sets

        // Go through all of the group members and mark in the
        // lookup-table, the group that each shape belongs to.
        length = objectNodeNamesArray.length();
        for( i=0; i<length; i++ ) {
            MIntArray groupTableIndicesArray;
            bool *objectGroupTable = objectGroupsTablePtr[i];
            int length2;
            recFindTransformDAGNodes( objectNodeNamesArray[i], groupTableIndicesArray );
            length2 = groupTableIndicesArray.length();
            for( j=0; j<length2; j++ ) {
                int groupIdx = groupTableIndicesArray[j];
                objectGroupTable[groupIdx] = true;
            }
        }
    }

    void ObjTranslator::freeLookupTables()
    //
    // Frees up all tables and arrays allocated by this plug-in.
    //
    {
        for ( int i=0; i<objectCount; i++ ) {
            if ( vertexTablePtr[i] != NULL ) {
                free( vertexTablePtr[i] );
            }
            if ( polygonTablePtr[i] != NULL ) {
                free( polygonTablePtr[i] );
            }
        }

        if( objectGroupsTablePtr != NULL ) {
            for ( int i=0; i<objectCount; i++ ) {
                if ( objectGroupsTablePtr[i] != NULL ) {
                    free( objectGroupsTablePtr[i] );
                }
            }
            free( objectGroupsTablePtr );
            objectGroupsTablePtr = NULL;
        }

        if ( vertexTablePtr != NULL ) {
            free( vertexTablePtr );
            vertexTablePtr = NULL;
        }
        if ( polygonTablePtr != NULL ) {
            free( polygonTablePtr );
            polygonTablePtr = NULL;
        }

        if ( lastSets != NULL ) {
            delete lastSets;
            lastSets = NULL;
        }

        if ( lastMaterials != NULL ) {
            delete lastMaterials;
            lastMaterials = NULL;
        }

        if ( sets != NULL ) {
            delete sets;
            sets = NULL;
        }

        if ( objectNames != NULL ) {
            delete objectNames;
            objectNames = NULL;
        }
    }

    bool ObjTranslator::lookup( MDagPath& dagPath,
                                int setIndex,
                                int compIdx,
                                bool isVtxIter )
    {

        if (isVtxIter) {
            vertexTable = vertexTablePtr[objectId];
            bool ret = *(vertexTable + numSets*compIdx + setIndex);
            return ret;
        }
        else  {
            polygonTable = polygonTablePtr[objectId];
            bool ret = *(polygonTable + numSets*compIdx + setIndex);
            return ret;
        }
    }

    void ObjTranslator::buildEdgeTable( MDagPath& mesh )
    {
        if ( !smoothing )
            return;

        // Create our edge lookup table and initialize all entries to NULL
        //
        MFnMesh fnMesh( mesh );
        edgeTableSize = fnMesh.numVertices();
        edgeTable = (EdgeInfoPtr*) calloc( edgeTableSize, sizeof(EdgeInfoPtr) );

        // Add entries, for each edge, to the lookup table
        //
        MItMeshEdge eIt( mesh );
        for ( ; !eIt.isDone(); eIt.next() )
        {
            bool smooth = eIt.isSmooth();
            addEdgeInfo( eIt.index(0), eIt.index(1), smooth );
        }

        // Fill in referenced polygons
        //
        MItMeshPolygon pIt( mesh );
        for ( ; !pIt.isDone(); pIt.next() )
        {
            int pvc = pIt.polygonVertexCount();
            for ( int v=0; v<pvc; v++ )
            {
                int a = pIt.vertexIndex( v );
                int b = pIt.vertexIndex( v==(pvc-1) ? 0 : v+1 );

                EdgeInfoPtr elem = findEdgeInfo( a, b );
                if ( NULL != elem ) {
                    int edgeId = pIt.index();

                    if ( INVALID_ID == elem->polyIds[0] ) {
                        elem->polyIds[0] = edgeId;
                    }
                    else {
                        elem->polyIds[1] = edgeId;
                    }

                }
            }
        }

        // Now create a polyId->smoothingGroup table
        //
        int numPolygons = fnMesh.numPolygons();
        polySmoothingGroups = (int*)malloc( sizeof(int) *  numPolygons );
        for ( int i=0; i< numPolygons; i++ ) {
            polySmoothingGroups[i] = NO_SMOOTHING_GROUP;
        }

        // Now call the smoothingAlgorithm to fill in the polySmoothingGroups
        // table.
        // Note: we have to traverse ALL polygons to handle the case
        // of disjoint polygons.
        //
        nextSmoothingGroup = 1;
        currSmoothingGroup = 1;
        for ( int pid=0; pid<numPolygons; pid++ ) {
            newSmoothingGroup = true;
            // Check polygon has not already been visited
            if ( NO_SMOOTHING_GROUP == polySmoothingGroups[pid] ) {
               if ( !smoothingAlgorithm(pid,fnMesh) ) {
                   // No smooth edges for this polygon so we set
                   // the smoothing group to NO_SMOOTHING_GROUP (off)
                   polySmoothingGroups[pid] = NO_SMOOTHING_GROUP;
               }
            }
        }
    }

    bool ObjTranslator::smoothingAlgorithm( int polyId, MFnMesh& fnMesh )
    {
        MIntArray vertexList;
        fnMesh.getPolygonVertices( polyId, vertexList );
        int vcount = vertexList.length();
        bool smoothEdgeFound = false;

        for ( int vid=0; vid<vcount;vid++ ) {
            int a = vertexList[vid];
            int b = vertexList[ vid==(vcount-1) ? 0 : vid+1 ];

            EdgeInfoPtr elem = findEdgeInfo( a, b );
            if ( NULL != elem ) {
                // NOTE: We assume there are at most 2 polygons per edge
                //       halfEdge polygons get a smoothing group of
                //       NO_SMOOTHING_GROUP which is equivalent to "s off"
                //
                if ( NO_SMOOTHING_GROUP != elem->polyIds[1] ) { // Edge not a border

                    // We are starting a new smoothing group
                    //
                    if ( newSmoothingGroup ) {
                        currSmoothingGroup = nextSmoothingGroup++;
                        newSmoothingGroup = false;

                        // This is a SEED (starting) polygon and so we always
                        // give it the new smoothing group id.
                        // Even if all edges are hard this must be done so
                        // that we know we have visited the polygon.
                        //
                        polySmoothingGroups[polyId] = currSmoothingGroup;
                    }

                    // If we have a smooth edge then this poly must be a member
                    // of the current smoothing group.
                    //
                    if ( elem->smooth ) {
                        polySmoothingGroups[polyId] = currSmoothingGroup;
                        smoothEdgeFound = true;
                    }
                    else { // Hard edge so ignore this polygon
                        continue;
                    }

                    // Find the adjacent poly id
                    //
                    int adjPoly = elem->polyIds[0];
                    if ( adjPoly == polyId ) {
                        adjPoly = elem->polyIds[1];
                    }

                    // If we are this far then adjacent poly belongs in this
                    // smoothing group.
                    // If the adjacent polygon's smoothing group is not
                    // NO_SMOOTHING_GROUP then it has already been visited
                    // so we ignore it.
                    //
                    if ( NO_SMOOTHING_GROUP == polySmoothingGroups[adjPoly] ) {
                        smoothingAlgorithm( adjPoly, fnMesh );
                    }
                    else if ( polySmoothingGroups[adjPoly] != currSmoothingGroup ) {
                        cerr << "Warning: smoothing group problem at polyon ";
                        cerr << adjPoly << endl;
                    }
                }
            }
        }
        return smoothEdgeFound;
    }

    void ObjTranslator::addEdgeInfo( int v1, int v2, bool smooth )
    //
    // Adds a new edge info element to the vertex table.
    //
    {
        EdgeInfoPtr element = NULL;

        if ( NULL == edgeTable[v1] ) {
            edgeTable[v1] = (EdgeInfoPtr)malloc( sizeof(struct EdgeInfo) );
            element = edgeTable[v1];
        }
        else {
            element = edgeTable[v1];
            while ( NULL != element->next ) {
                element = element->next;
            }
            element->next = (EdgeInfoPtr)malloc( sizeof(struct EdgeInfo) );
            element = element->next;
        }

        // Setup data for new edge
        //
        element->vertId     = v2;
        element->smooth     = smooth;
        element->next       = NULL;

        // Initialize array of id's of polygons that reference this edge.
        // There are at most 2 polygons per edge.
        //
        element->polyIds[0] = INVALID_ID;
        element->polyIds[1] = INVALID_ID;
    }

    EdgeInfoPtr ObjTranslator::findEdgeInfo( int v1, int v2 )
    //
    // Finds the info for the specified edge.
    //
    {
        EdgeInfoPtr element = NULL;
        element = edgeTable[v1];

        while ( NULL != element ) {
            if ( v2 == element->vertId ) {
                return element;
            }
            element = element->next;
        }

        if ( element == NULL ) {
            element = edgeTable[v2];

            while ( NULL != element ) {
                if ( v1 == element->vertId ) {
                    return element;
                }
                element = element->next;
            }
        }

        return NULL;
    }

    void ObjTranslator::destroyEdgeTable()
    //
    // Free up all of the memory used by the edgeTable.
    //
    {
        if ( !smoothing )
            return;

        EdgeInfoPtr element = NULL;
        EdgeInfoPtr tmp = NULL;

        for ( int v=0; v<edgeTableSize; v++ )
        {
            element = edgeTable[v];
            while ( NULL != element )
            {
                tmp = element;
                element = element->next;
                free( tmp );
            }
        }

        if ( NULL != edgeTable ) {
            free( edgeTable );
            edgeTable = NULL;
        }

        if ( NULL != polySmoothingGroups ) {
            free( polySmoothingGroups );
            polySmoothingGroups = NULL;
        }
    }

    MStatus ObjTranslator::exportSelected( )
    {
        MStatus status;
        MString filename;

        initializeSetsAndLookupTables( false );

        // Create an iterator for the active selection list
        //
        MSelectionList slist;
        MGlobal::getActiveSelectionList( slist );
        MItSelectionList iter( slist );

        if (iter.isDone()) {
            fprintf(stderr,"Error: Nothing is selected.\n");
            return MS::kFailure;
        }

        // We will need to interate over a selected node's heirarchy
        // in the case where shapes are grouped, and the group is selected.
        MItDag dagIterator( MItDag::kDepthFirst, MFn::kInvalid, &status);

        // reset counters
        v = vt = vn = 0;
        voff = vtoff = vnoff = 0;

        // Selection list loop
        for ( ; !iter.isDone(); iter.next() )
        {
            MDagPath objectPath;
            // get the selected node
            status = iter.getDagPath( objectPath);

            // reset iterator's root node to be the selected node.
            status = dagIterator.reset (objectPath.node(),
                                        MItDag::kDepthFirst, MFn::kInvalid );

            // DAG iteration beginning at at selected node
            for ( ; !dagIterator.isDone(); dagIterator.next() )
            {
                MDagPath dagPath;
                MObject  component = MObject::kNullObj;
                status = dagIterator.getPath(dagPath);

                if (!status) {
                    fprintf(stderr,"Failure getting DAG path.\n");
                    freeLookupTables();
                    return MS::kFailure;
                }

                if (status )
                {
                    // skip over intermediate objects
                    //
                    MFnDagNode dagNode( dagPath, &status );
                    if (dagNode.isIntermediateObject())
                    {
                        continue;
                    }

                    if (dagPath.hasFn(MFn::kNurbsSurface))
                    {
                        status = MS::kSuccess;
                        fprintf(stderr,"Warning: skipping Nurbs Surface.\n");
                    }
                    else if ((  dagPath.hasFn(MFn::kMesh)) &&
                             (  dagPath.hasFn(MFn::kTransform)))
                    {
                        // We want only the shape,
                        // not the transform-extended-to-shape.
                        continue;
                    }
                    else if (  dagPath.hasFn(MFn::kMesh))
                    {
                        // Build a lookup table so we can determine which
                        // polygons belong to a particular edge as well as
                        // smoothing information
                        //
                        buildEdgeTable( dagPath );

                        status = OutputPolygons(dagPath, component);
                        objectId++;
                        if (status != MS::kSuccess) {
                            fprintf(stderr, "Error: exporting geom failed, check your selection.\n");
                            freeLookupTables();
                            destroyEdgeTable(); // Free up the edge table
                            return MS::kFailure;
                        }
                        destroyEdgeTable(); // Free up the edge table
                    }
                    voff = v;
                    vtoff = vt;
                    vnoff = vn;
                }
            }
        }

        freeLookupTables();

        return status;
    }

    MStatus ObjTranslator::exportAll( )
    {
        MStatus status = MS::kSuccess;

        initializeSetsAndLookupTables( true );

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

        if ( MS::kSuccess != status) {
            fprintf(stderr,"Failure in DAG iterator setup.\n");
            return MS::kFailure;
        }
        // reset counters
        v = vt = vn = 0;
        voff = vtoff = vnoff = 0;

        for ( ; !dagIterator.isDone(); dagIterator.next() )
        {
            MDagPath dagPath;
            MObject  component = MObject::kNullObj;
            status = dagIterator.getPath(dagPath);

            if (!status) {
                fprintf(stderr,"Failure getting DAG path.\n");
                freeLookupTables();
                return MS::kFailure;
            }

            // skip over intermediate objects
            //
            MFnDagNode dagNode( dagPath, &status );
            if (dagNode.isIntermediateObject())
            {
                continue;
            }

            if ((  dagPath.hasFn(MFn::kNurbsSurface)) &&
                (  dagPath.hasFn(MFn::kTransform)))
            {
                status = MS::kSuccess;
                fprintf(stderr,"Warning: skipping Nurbs Surface.\n");
            }
            else if ((  dagPath.hasFn(MFn::kMesh)) &&
                     (  dagPath.hasFn(MFn::kTransform)))
            {
                // We want only the shape,
                // not the transform-extended-to-shape.
                continue;
            }
            else if (  dagPath.hasFn(MFn::kMesh))
            {
                // Build a lookup table so we can determine which
                // polygons belong to a particular edge as well as
                // smoothing information
                //
                buildEdgeTable( dagPath );

                // Now output the polygon information
                //
                status = OutputPolygons(dagPath, component);
                objectId++;
                if (status != MS::kSuccess) {
                    fprintf(stderr,"Error: exporting geom failed.\n");
                    freeLookupTables();
                    destroyEdgeTable(); // Free up the edge table
                    return MS::kFailure;
                }
                destroyEdgeTable(); // Free up the edge table
            }
            voff = v;
            vtoff = vt;
            vnoff = vn;
        }

        freeLookupTables();

        return status;
    }

    void ObjTranslator::recFindTransformDAGNodes( MString& nodeName, MIntArray& transformNodeIndicesArray )
    {
        // To handle Maya groups we traverse the hierarchy starting at
        // each objectNames[i] going towards the root collecting transform
        // nodes as we go.
        MStringArray result;
        MString cmdStr = "listRelatives -ap " + nodeName;
        MGlobal::executeCommand( cmdStr, result );

        if( result.length() == 0 )
            // nodeName must be at the root of the DAG.  Stop recursing
            return;

        for( unsigned int j=0; j<result.length(); j++ ) {
            // check if the node result[i] is of type transform
            MStringArray result2;
            MGlobal::executeCommand( "nodeType " + result[j], result2 );

            if( result2.length() == 1 && result2[0] == "transform" ) {
                // check if result[j] is already in result[j]
                bool found=false;
                unsigned int i;
                for( i=0; i<transformNodeNameArray.length(); i++) {
                    if( transformNodeNameArray[i] == result[j] ) {
                        found = true;
                        break;
                    }
                }

                if( !found ) {
                    transformNodeIndicesArray.append(transformNodeNameArray.length());
                    transformNodeNameArray.append(result[j]);
                }
                else {
                    transformNodeIndicesArray.append(i);
                }
                recFindTransformDAGNodes(result[j], transformNodeIndicesArray);
            }
        }
    }