Maya API How-To #28

Back · Previous Maya

How do I read and write a "componentList" attribute, such as '.inputComponents' found on polygon modifier nodes?

Reading

It is necessary to use the API in order to read the components specified in a componentList attribute, because it is not possible to do so via MEL (see below).

The example below is an excerpt from a MPxCommand-derived plug-in which reads the component indices from a "componentList" attribute.

// ----------------------------------------------------------------------
//  ReadComponents
// ----------------------------------------------------------------------
/// From Maya API How-To #28, by Bryan Ewert
///
/// Reads the '.inputComponents' attribute for the specified node and
/// writes its contents - in a representative form - to the string array
/// results of a MPxCommand.
///
/// \param  The node from which to query the '.inputComponents' attribute.
///
/// \return Maya status; the usual.
// ----------------------------------------------------------------------
MStatus InputComponentsCommand::ReadComponents( MObject& node ) const
{
    MStatus status;
    MStringArray results;

    MFnDependencyNode fnDependNode( node );

    // Can you believe that Alias switches between '.ics' and '.ic' as the 
    // short name for '.inputComponents' on different nodes? Tsk tsk, careless.
    //
    MPlug icsPlug = fnDependNode.findPlug( INPUTCOMPONENTS_NAME, &status );
    if ( MS::kSuccess == status )
    {
        MObject icsData;

        MString componentType;
        MIntArray elements;
        MIntArray sIndexArray;
        MIntArray tIndexArray;
        MIntArray uIndexArray;
        MIntArray vIndexArray;
        unsigned int e;

        // Read the data object from the '.inputComponents' plug.
        // If this returns an error it means the data is _empty_,
        // and not that the command failed to execute properly.
        //
        MStatus dataStatus = icsPlug.getValue( icsData );
        if ( MS::kSuccess == dataStatus )
        {
            // Wrap the data in its function set.
            //
            MFnComponentListData fnComponent( icsData );
            unsigned int numComponents = fnComponent.length();

            // Append each component's representation to the command results.
            //
            while ( numComponents-- )
            {
                MObject component = fnComponent[numComponents];

                componentType = component_type( component );

                // Examples of single indexed components: poly vertices, poly edges,
                // poly faces, NURBS curve CVs or edit points.
                //
                if ( component.hasFn( MFn::kSingleIndexedComponent ) )
                {
                    MFnSingleIndexedComponent fnSingleComponent( component );

                    fnSingleComponent.getElements( elements );
                    for ( e = 0; e < elements.length(); e++ )
                    {
                        appendToResult( componentType + "[" + elements[e] + "]" );
                    }
                }
                else
                // Examples of double indexed components: NURBS Surface CVs or
                // edit points.
                //
                if ( component.hasFn( MFn::kDoubleIndexedComponent ) )
                {
                    MFnDoubleIndexedComponent fnDoubleComponent( component );

                    fnDoubleComponent.getElements( uIndexArray, vIndexArray );
                    for ( e = 0; e < uIndexArray.length(); e++ )
                    {
                        // Line split for readability in online How-To.
                        appendToResult( componentType + "[" + uIndexArray[e] + "]" +
                                                        "[" + vIndexArray[e] + "]" );
                    }
                }
                else
                // Examples of triple indexed components: Lattice points.
                //
                if ( component.hasFn( MFn::kTripleIndexedComponent ) )
                {
                    MFnTripleIndexedComponent fnTripleComponent( component );

                    fnTripleComponent.getElements( sIndexArray, tIndexArray, uIndexArray );
                    for ( e = 0; e < uIndexArray.length(); e++ )
                    {
                        // Line split for readability in online How-To.
                        appendToResult( componentType + "[" + sIndexArray[e] + "]" +
                                                        "[" + tIndexArray[e] + "]" +
                                                        "[" + uIndexArray[e] + "]" );
                    }
                }
                else
                // Subdivision surface face components seem to fit as a double indexed component,
                // but the API offers no function set for them (as of Maya 6.0.1).
                //
                if ( component.hasFn( MFn::kSubdivFaceComponent ) )
                {
                    // Line split for readability in online How-To.
                    MGlobal::displayInfo( "I wish I could help, but Maya's API doesn't appear to "
                                          "support the MFn::kSubdivFaceComponent type." );
                    MGlobal::displayInfo( "It _should_ be a kDoubleIndexedComponent, but it's not." );
                }
            }
        }
    }

    return status;
}

Writing

As above, the following is an excerpt from a MPxCommand-derived plug-in which writes a list of component indices to a "componentList" attribute.

Note the restriction described in the code below. This function will not work in Maya 4, but has been tested successfully in Maya 6.

// ----------------------------------------------------------------------
//  WriteComponents
// ----------------------------------------------------------------------
/// From Maya API How-To #28, by Bryan Ewert
///
/// Writes a "componentList" attribute, assigning the specified elements
/// of the specified component type, to the '.inputComponents' attribute
/// on the specified node.
///
/// \note   This will fail under Maya 4. See comments in code below.
///
/// \param  node: The node whose '.inputComponents' attribute will be
///               modified.
/// \param  componentType: The component type.
/// \param  elements: The list of elements. Can't be (const) because
///                   Maya's MFnSingleIndexedComponent::setElements()
///                   is not (const) correct.
///
/// \return Maya status; the usual.
// ----------------------------------------------------------------------
MStatus InputComponentsCommand::WriteComponents( MObject& node,
                                                 MFn::Type componentType,
                                                 MIntArray& elements ) const
{
    if ( componentType == MFn::kInvalid )
    {
        MGlobal::displayError( "Invalid component type." );
        return MS::kInvalidParameter;
    }

    MStatus status;

    MFnDependencyNode fnDependNode( node );

    // Can you believe that Alias switches between '.ics' and '.ic' as the 
    // short name for '.inputComponents' on different nodes? Tsk tsk, careless.
    //
    MPlug icsPlug = fnDependNode.findPlug( INPUTCOMPONENTS_NAME, &status );
    if ( MS::kSuccess == status )
    {
        // Create a component list.
        //
        MFnComponentListData fnComponentList;
        MObject componentData = fnComponentList.create();

        // Create an appropriate component.
        //
        MObject component( MObject::kNullObj );

        switch ( componentType )
        {
            case MFn::kMeshVertComponent:
            case MFn::kMeshEdgeComponent:
            case MFn::kMeshPolygonComponent:
            {
                // Create a single component.
                //
                MFnSingleIndexedComponent fnSingleComponent;
                component = fnSingleComponent.create( componentType );

                fnSingleComponent.addElements( elements );

                break;
            }
        }

        if ( MObject::kNullObj != component )
        {
            // Add the component to the list data.
            //
            // Note: This will fail under Maya 4. Maya "crashes" internally
            //       and aborts execution of the plug-in when attempting to add
            //       the component MObject to the component list.
            //
            // Tested successfully under Maya 6.0.1.
            //
            fnComponentList.add( component );

            // Write the data to the attribute.
            //
            icsPlug.setValue( componentData );
        }
    }

    return status;
}

"componentList" Attributes and MEL

Reading

It is not possible to read the components from a componentList attribute via MEL.

getAttr polySubdFace1.inputComponents;
// Error: line 1: The data is not a numeric or string value, and cannot be displayed. //

Writing

Writing a componentList attribute is possible via MEL - necessarily so, since it is required for the loading of MayaAscii scene files. To assign a componentList you must specify the attribute type for the "setAttr" command, using the '-type' flag, as "componentList". You must specify, as the first argument for the componentList, the number of components which are to be assigned. This is followed by one or more strings which define the components.

I've also found that it is sometimes necessary to force Maya to recalculate the dependency graph using the "dgdirty" command in order to see the results. You can specify the input attribute on the affected node - e.g. '.inputPolymesh', for a polyModifier-based node - or simply mark the whole node as dirty. The former is a little more efficient, but it's doubtful you'd ever notice the difference unless you were assigning hundreds of nodes.

string $node = "polySubdFace1";

// Assign faces 0 and 2 to be subdivided by the polySubdFace node.
//
setAttr -type "componentList" ( $node + ".inputComponents" ) 2 "f[0]" "f[2]";
dgdirty ( $node + ".inputPolymesh" );
Note that, if you use the compressed form of specifying a range of attributes - e.g. "e[0:3]" - that this counts as a single entry in the component list.
string $node = "polySubdEdge1";

// Assign faces 0 through 3 inclusive to be subdivided by the polySubdFace node.
//
setAttr -type "componentList" ( $node + ".inputComponents" ) 1 "e[0:3]";
dgdirty ( $node + ".inputPolymesh" );

Running on Empty?

Note that if you specify an empty component list, many nodes will cause Maya to generate a warning.

string $node = "polySubdEdge1";

setAttr -type "componentList" ( $node + ".inputComponents" ) 0;
// Warning: Can't perform polySubdEdge1 on disabled selection //

Related How-To's

28 Nov 2004