Your Ad Here

DAG walking example

The following example is the scanDagSyntaxCmd example. It demonstrates iterating through the DAG in either a depth first, or a breadth first manner. This code makes a good basis for many DAG walking plug-ins, in particular those written as file translators.

As with previous examples, the list of include files is omitted for brevity. See the scanDagSyntaxCmd.cpp file in devkit/plug-ins for the complete example.

class scanDagSyntax: public MPxCommand 
{ 
public: 
                    scanDagSyntax() {}; 
    virtual         ~scanDagSyntax(); 
    static void*    creator(); 
    static MSyntax  newSyntax(); 
    virtual MStatus doIt( const MArgList& ); 
This is a simple example so the undoIt() and redoIt() methods are not implemented.  
private: 
    MStatus         parseArgs( const MArgList& args, 
                               MItDag::TraversalType& traversalType, 
                               MFn::Type& filter, bool & quiet); 
    MStatus         doScan( const MItDag::TraversalType traversalType, 
                            MFn::Type filter, bool quiet); 
    void            printTransformData(const MDagPath& dagPath, bool quiet); 
}; 
scanDagSyntax::~scanDagSyntax() {} 
void* scanDagSyntax::creator() 
{ 
    return new scanDagSyntax; 
} 
    MSyntax scanDagSyntax::newSyntax() 
{ 
    MSyntax syntax; 
    syntax.addFlag(kBreadthFlag, kBreadthFlagLong); 
    syntax.addFlag(kDepthFlag, kDepthFlagLong); 
    syntax.addFlag(kCameraFlag, kCameraFlagLong); 
    syntax.addFlag(kLightFlag, kLightFlagLong); 
    syntax.addFlag(kNurbsSurfaceFlag, kNurbsSurfaceFlagLong); 
    syntax.addFlag(kQuietFlag, kQuietFlagLong); 
    return syntax; 
} 
MStatus scanDagSyntax::doIt( const MArgList& args ) 
{ 
    MItDag::TraversalType   traversalType = MItDag::kDepthFirst; 
    MFn::Type               filter        = MFn::kInvalid; 
    MStatus                 status; 
    bool                    quiet = false; 

The DAG iterator being used later can be set to only iterate across objects of a particular type (for example cameras). If the filter mode is set to MFn::kInvalid, no filtering will be done and all DAG nodes will be iterated across.

    status = parseArgs ( args, traversalType, filter, quiet ); 
    if (!status) 
        return status; 
    return doScan( traversalType, filter, quiet); 
}; 
The doIt() method is simply calling a few auxiliary methods which do the real work. 
MStatus scanDagSyntax::parseArgs( const MArgList& args, 
                                  MItDag::TraversalType& traversalType, 
                                  MFn::Type& filter, 
                                  bool & quiet) 
{ 
    MStatus         stat; 
    MArgDatabase    argData(syntax(), args); 
    MString         arg; 
    if (argData.isFlagSet(kBreadthFlag)) 
        traversalType = MItDag::kBreadthFirst; 
    else if (argData.isFlagSet(kDepthFlag)) 
        traversalType = MItDag::kDepthFirst; 
    if (argData.isFlagSet(kCameraFlag)) 
        filter = MFn::kCamera; 
    else if (argData.isFlagSet(kLightFlag)) 
        filter = MFn::kLight; 
    else if (argData.isFlagSet(kNurbsSurfaceFlag)) 
        filter = MFn::kNurbsSurface; 
         
    if (argData.isFlagSet(kQuietFlag)) 
        quiet = true; 
         
    return stat; 
} 

The DAG iterator can either iterate across the DAG depth first or breadth first. This simple example only filters on cameras, lights, and NURBS surfaces, but it is possible to iterate across any type in MFn::Type.

MStatus scanDagSyntax::doScan( const MItDag::TraversalType traversalType, 
                               MFn::Type filter, 
                               bool quiet) 
{  

This method will do all the real work of this command. It uses the traversal type (depth or breadth first) and the filter type to initialize an MItDag (a DAG iterator) to walk across the DAG.

    MStatus status; 
    MItDag dagIterator( traversalType, filter, &status); 

The DAG iterator is initialized looking at the DAG. It will walk the DAG downwards.

    if ( !status) { 
        status.perror("MItDag constructor"); 
        return status; 
    } 
    //  Scan the entire DAG and output the name and depth of each node 
    if (traversalType == MItDag::kBreadthFirst) 
        if (!quiet) 
            cout << endl << "Starting Breadth First scan of the Dag"; 
    else 
        if (!quiet) 
            cout << endl << "Starting Depth First scan of the Dag"; 

Breadth first walking of the DAG means that siblings will be visited before children, while depth first means that children will be visited before siblings.

    switch (filter) { 
        case MFn::kCamera: 
            if (!quiet) 
                cout << ": Filtering for Cameras\n"; 
            break; 
        case MFn::kLight: 
            if (!quiet) 
                cout << ": Filtering for Lights\n"; 
            break; 
        case MFn::kNurbsSurface: 
            if (!quiet) 
                cout << ": Filtering for Nurbs Surfaces\n"; 
            break; 
        default: 
            cout << endl; 
    } 
     
    int objectCount = 0; 
    for ( ; !dagIterator.isDone(); dagIterator.next() ) { 
        MDagPath dagPath; 
        status = dagIterator.getPath(dagPath); 
        if ( !status ) { 
            status.perror("MItDag::getPath"); 
            continue; 
        } 

MItDag::getPath() gets the reference to the object that the iterator is currently on. This DAG path can then be used in a function set to operate on the object. In general it is not a good idea to rearrange the DAG from with an iterator.

        MFnDagNode dagNode(dagPath, &status); 
        if ( !status ) { 
            status.perror("MFnDagNode constructor"); 
            continue; 
        } 
        if (!quiet) 
            cout << dagNode.name() << ": " << dagNode.typeName() << endl; 
        if (!quiet) 
            cout << "  dagPath: " << dagPath.fullPathName() << endl; 
        objectCount += 1; 
        if (dagPath.hasFn(MFn::kCamera)) { 

This determines if the object the iterator is currently visiting is a camera or not, and if it is, the following code outputs camera specific information.

            MFnCamera camera (dagPath, &status); 
            if ( !status ) { 
                status.perror("MFnCamera constructor"); 
                continue; 
            } 
            // Get the translation/rotation/scale data 
            printTransformData(dagPath, quiet); 
            // Extract some interesting Camera data 
            if (!quiet) 
            { 
                cout << "  eyePoint: " 
                     << camera.eyePoint(MSpace::kWorld) << endl; 
                cout << "  upDirection: " 
                     << camera.upDirection(MSpace::kWorld) << endl; 
                cout << "  viewDirection: " 
                     << camera.viewDirection(MSpace::kWorld) << endl; 
                cout << "  aspectRatio: " << camera.aspectRatio() << endl; 
                cout << "  horizontalFilmAperture: " 
                     << camera.horizontalFilmAperture() << endl; 
                cout << "  verticalFilmAperture: " 
                     << camera.verticalFilmAperture() << endl; 
            } 
        } else if (dagPath.hasFn(MFn::kLight)) { 

If the object is a light, this code outputs light specific information.

            MFnLight light (dagPath, &status); 
            if ( !status ) { 
                status.perror("MFnLight constructor"); 
                continue; 
            } 
            // Get the translation/rotation/scale data 
            printTransformData(dagPath, quiet); 
            // Extract some interesting Light data 
            MColor color; 
            color = light.color(); 
            if (!quiet) 
            { 
                cout << "  color: [" 
                     << color.r << ", " 
                     << color.g << ", " 
                     << color.b << "]\n"; 
            } 
            color = light.shadowColor(); 
            if (!quiet) 
            { 
                cout << "  shadowColor: [" 
                     << color.r << ", " 
                     << color.g << ", " 
                     << color.b << "]\n"; 
                cout << "  intensity: " << light.intensity() << endl; 
            } 
        } else if (dagPath.hasFn(MFn::kNurbsSurface)) { 
Finally, if the object is a NURBS surface, surface specific information is output. 
            MFnNurbsSurface surface (dagPath, &status); 
            if ( !status ) { 
                status.perror("MFnNurbsSurface constructor"); 
                continue; 
            } 
            // Get the translation/rotation/scale data 
            printTransformData(dagPath, quiet); 
            // Extract some interesting Surface data 
            if (!quiet) 
            { 
                cout << "  numCVs: " 
                     << surface.numCVsInU() 
                     << " * " 
                     << surface.numCVsInV() 
                     << endl; 
                cout << "  numKnots: " 
                     << surface.numKnotsInU() 
                     << " * " 
                     << surface.numKnotsInV() 
                     << endl; 
                cout << "  numSpans: " 
                     << surface.numSpansInU() 
                     << " * " 
                     << surface.numSpansInV() 
                     << endl; 
            } 
        } else { 

For any other type of DAG node, just the transformation information is printed.

            // Get the translation/rotation/scale data 
            printTransformData(dagPath, quiet); 
        } 
    } 
    if (!quiet) 
    { 
        cout.flush(); 
    } 
    setResult(objectCount); 
    return MS::kSuccess; 
} 
void scanDagSyntax::printTransformData(const MDagPath& dagPath, bool quiet) 
{ 
This method simply determines the transformation information on the DAG node and 
prints it out. 
    MStatus     status; 
    MObject     transformNode = dagPath.transform(&status); 
    // This node has no transform - i.e., it’s the world node 
    if (!status && status.statusCode () == MStatus::kInvalidParameter) 
        return; 
    MFnDagNode  transform (transformNode, &status); 
    if (!status) { 
        status.perror("MFnDagNode constructor"); 
        return; 
    } 
    MTransformationMatrix   matrix (transform.transformationMatrix()); 
    if (!quiet) 
    { 
        cout << "  translation: " << matrix.translation(MSpace::kWorld) 
             << endl; 
    } 
    double                                  threeDoubles[3]; 
    MTransformationMatrix::RotationOrder    rOrder; 
    matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld); 
    if (!quiet) 
    { 
        cout << "  rotation: [" 
             << threeDoubles[0] << ", " 
             << threeDoubles[1] << ", " 
             << threeDoubles[2] << "]\n"; 
    } 
    matrix.getScale (threeDoubles, MSpace::kWorld); 
    if (!quiet) 
    { 
        cout << "  scale: [" 
             << threeDoubles[0] << ", " 
             << threeDoubles[1] << ", " 
             << threeDoubles[2] << "]\n"; 
    } 
} 
MStatus initializePlugin( MObject obj ) 
{  
    MStatus status; 
    MFnPlugin plugin ( obj, "Autodesk - Example", "2.0", "Any" ); 
    status = plugin.registerCommand( "scanDagSyntax",  
                                     scanDagSyntax::creator, 
                                     scanDagSyntax::newSyntax );  
     
    return status; 
} 
MStatus uninitializePlugin( MObject obj ) 
{ 
    MStatus status; 
    MFnPlugin plugin( obj ); 
    status = plugin.deregisterCommand( "scanDagSyntax" ); 
    return status; 
} 

The plug-in finishes with the usual initializePlugin and uninitializePlugin methods.

This plug-in can easily be modified for use as a file translator, or any other type of plug-in which needs to visit the DAG nodes in the model.

 

Return to Autodesk Index


Your Ad Here