objExport/objExport.cpp
 
 
 
objExport/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);
                }
        }
}