Generic Attributes

 
 
 

In this section of the Maya API documentation, we will describe parts of the Maya API that are important but are not large enough for an entire chapter.

A generic attribute (MFnGenericAttribute) is one that can accept several different types of data. A generic attribute is similar to a typed attribute (MFnTypedAttribute) except that it has a list of types that it considers valid.

Generic attributes are useful for re-using the same attribute for multiple purposes. For example, when writing a plug-in intersection node, an attribute will be used for storing information regarding the object(s) that are being intersected. A plug-in can create a generic attribute that can accept either a mesh or a surface for use in the intersection calculation. If a generic attribute were not used, then the plug-in would have to create two separate attributes: one for the mesh and one for the surface. An additional advantage of generic attributes is that multiple nodes of the same type can have their generic attribute type set independently. For example, in the intersection example just described, you can create two nodes of the same type, with the generic attribute of one set to a mesh and the other set to a surface.

Creation

Generic attributes for proxy plug-in nodes are created using the initialize() method of the plug-in. In addition to creating the attribute, the initialize() method will add the attribute to the node and state how this attribute can affect or be affected by other attributes in the node.

The following example, genericAttributeNode (available in the devkit), demonstrates how to create a generic attribute as an output:

// Adds a generic attribute that accepts a float, float2, float3
// Called from node initialization method.
MStatus genericAttributeNode::addComplexFloatGenericAttribute( 
    MObject& attrObject,
    const char *longName, const char *shortName )
{
    // Create the generic attribute and set the 3 accepts types
    MFnGenericAttribute gAttr; 
    attrObject = gAttr.create( longName, shortName ); 
    MStatus status = gAttr.addAccept(MFnNumericData::kFloat); 
    CHECK_MSTATUS( status );
    status = gAttr.addAccept(MFnNumericData::k2Float); 
    CHECK_MSTATUS( status );
    status = gAttr.addAccept(MFnNumericData::k3Float); 
    CHECK_MSTATUS( status );
    gAttr.setWritable(false);
    gAttr.setStorable( false );

    // Add the attribute to the node
    status = addAttribute( attrObject );
    CHECK_MSTATUS( status );
    return MS::kSuccess;
}

// Node initialization method
MStatus genericAttributeNode::initialize()
{
    MFnNumericAttribute nAttr;
    MStatus status;
    // single float attribute affecting a generic attribute
    gInputInt = nAttr.create( "gInputInt", "gii",
        MFnNumericData::kInt, 0, &status );
    nAttr.setStorable(true);
    nAttr.setKeyable(true);
    CHECK_MSTATUS( status );
    status = addAttribute( gInputInt );
    CHECK_MSTATUS( status );
    if ( !addComplexFloatGenericAttribute(
        gOutputFloat_2Float_3Float,
        "gOutputFloat_2Float_3Float", "gof2f3f" ) )
        return MS::kFailure;
    status = attributeAffects( 
        gInputInt, gOutputFloat_2Float_3Float );
    CHECK_MSTATUS( status );

    return MS::kSuccess;
}

In this example, an input attribute gInputInt affects a generic output attribute gOutputFloat_2Float_3Float. The output attribute is capable of storing one of a float, float2 and float3 and this is set by calling MFnGenericAttribute::addAccept().

Computing Generic Attributes

Assuming that a plug-in’s generic attributes have been added correctly to a node, they will be available for use in the node’s compute method. Within compute, generic attribute information can be read and updated using standard Maya API calls. Additionally, the type contained in a generic attribute can be modified. In our example below, the generic attribute can store one of a float, float2 and float3. At compute time, the float stored in this generic attribute can be converted to a float3.

//
// Compute that will update the generic attribute
// when requested.
//
MStatus genericAttributeNode::compute( const MPlug& plug, MDataBlock& data )
{
    MStatus returnStatus;
    // Generic attribute plug
    if ( plug == gOutputFloat_2Float_3Float )
    {
        // attribute affecting generic attribute case. Based on the
        // input attribute, we modify the output generic attribute
        MDataHandle inputDataInt = data.inputValue( gInputInt );
        int inputInt = inputDataInt.asInt();

        // Get the output handle
        MDataHandle outputData = data.outputValue( plug );	
        bool isGenericNumeric = false;
        bool isGenericNull = false;
        // Is the output handle generic data
        if ( outputData.isGeneric( isGenericNumeric, isGenericNull ) )
        {
            // Based on the inputHandle, update the generic
            // output handle
            if ( inputInt == 1 )
                outputData.setGenericBool( false, true );
            else if ( inputInt == 2 )
                outputData.setGenericBool( true, true );
            else if ( inputInt == 3 )
                outputData.setGenericChar( 254, true );
            else if ( inputInt == 4 )
                outputData.setGenericDouble( 3.145, true );
            else if ( inputInt == 5 )
                outputData.setGenericFloat( 9.98, true );	
            else if ( inputInt == 6 )
                outputData.setGenericShort( 3245, true );
            else if ( inputInt == 7 )
                outputData.setGenericInt( 32768, true );
            else if ( inputInt == 8 )
            {
                MFnNumericData numericData;
                MObject obj = numericData.create(
                    MFnNumericData::k2Float, &returnStatus );
                CHECK_MSTATUS( returnStatus );
                returnStatus = numericData.setData( 
                    (float)1.5, (float)6.7 );
                CHECK_MSTATUS( returnStatus );
                outputData.set( obj );
            }
            else if ( inputInt == 9 )
            {
                MFnNumericData numericData;
                MObject obj = numericData.create(
                    MFnNumericData::k3Float, &returnStatus );
                CHECK_MSTATUS( returnStaus );
                returnStatus = numericData.setData( (float)2.5, (float)8.7, (float)2.3345 );
                CHECK_MSTATUS( returnStatus );
                outputData.set( obj );
            }
            else if ( inputInt == 10 )
            {
                outputData.setGenericInt( 909, true );
            }							
            // Mark the data clean
            outputData.setClean();
            data.setClean( gOutputFloat_2Float_3Float );
        }
    } 
    else 
    {
        return MS::kUnknownParameter;
    }
    return MS::kSuccess;
}

In this example, we first obtain the value of the gInputInt attribute. This int value will be used for setting the value and type of the generic output attribute gOutputFloat_2Float_3Float. When the data handle is extracted from the data block for the generic attribute, the isGeneric() method is called to ensure that the compute code is dealing with a generic attribute. There are two parameters for the isGeneric() method. The first is isNumeric and a result of true signifies that the handle contains simple numeric information such as char, float, etc. If isNumeric is false and isNull is not true, then the handle contains the more complex types of numerical information, such as double2 or float2, as accessible through the MFnNumericData function set. (The isNumeric and isNull parameters are not used in the code example above.)

It is not possible to distinguish the singleton generic types from one another. There is no method that can be called to return the result of whether the singleton generic attribute is a float, char or double, etc.

Creative Commons License Except where otherwise noted, this work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License