API Guide > Writing a Software Shading Node >

# InterpNode example code walkthrough

Shading node plug-ins rely on the usage of Compound Attributes and Simple Attributes. The mapping of data between rendering samplers and shading networks is by attribute name. This approach is straightforward and easy to learn and remember, and general enough to work with both the present rendering requirements and future enhancements.

All rendering attributes for which a plug-in is interested has been pre-computed for the current sample being considered. The "datablock" argument that is passed into the plug-in’s compute() method contains the rendering attribute information the node has requested. When the plug-in is evaluated by the dependency graph it also passes in a "plug" argument for the specific attribute it wants to evaluate. To optimize evaluations, you need to check for only the output attributes you defined in your plug-in.

This example plug-in node has 20 attributes (aside from its id attribute).

- Two attributes are color input attributes that are built as a compound attribute from three float attributes that represent red, green, and blue (eight total attributes).
- One attribute is a color output attribute that is built as a compound attribute from three float attributes that represent red, green, and blue (four total attributes).
- One attribute is a surface normal that is built as a compound attribute from three float attributes that represent the vector components in x, y, and z (four total attributes).
- One attribute is the position of the geometry in camera space built as a compound attribute from three float attributes that represent the current sample point in x, y, and z (4 total attributes).

The node interpolates between two colors based on the direction of the surface normal it gets from the datablock, and uses the compute() method in that class to derive a result color that is placed into the output color attribute.

Derivation

    class InterpNode : public MPxNode
    {
    public:
        InterpNode();
        virtual ~InterpNode();
        virtual MStatus compute( const MPlug&, MDataBlock& );
        static void * creator();
        static MStatus initialize();
        static MTypeId id;
    protected:
        static MObject InputValue;
        static MObject color1R,color1G,color1B,color1;
        static MObject color2R,color2G,color2B,color2;
        static MObject aNormalCameraX, aNormalCameraY,
        aNormalCameraZ, aNormalCamera;
        static MObject aPointCameraX, aPointCameraY,
        aPointCameraZ, aPointCamera;
        static MObject aOutColorR, aOutColorG, aOutColorB,
        aOutColor;
    };
    MObject InterpNode::InputValue;
    MObject InterpNode::color1R;
    MObject InterpNode::color1G;
    MObject InterpNode::color1B;
    MObject InterpNode::color1;
    MObject InterpNode::color2R;
    MObject InterpNode::color2G;
    MObject InterpNode::color2B;
    MObject InterpNode::color2;
    MObject InterpNode::aNormalCameraX;
    MObject InterpNode::aNormalCameraY;
    MObject InterpNode::aNormalCameraZ;
    MObject InterpNode::aNormalCamera;
    MObject InterpNode::aPointCameraX;
    MObject InterpNode::aPointCameraY;
    MObject InterpNode::aPointCameraZ;
    MObject InterpNode::aPointCamera;
    MObject InterpNode::aOutColorR;
    MObject InterpNode::aOutColorG;
    MObject InterpNode::aOutColorB;
    MObject InterpNode::aOutColor;

Constructor/Destructor

    InterpNode::InterpNode() { }
    InterpNode::~InterpNode() { }

Creator

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

initializePlugin/uninitializePlugin

    MStatus initializePlugin( MObject obj )
    {
        const MString UserClassify( "utility/general" );
        MFnPlugin plugin( obj, "Autodesk", "1.0",
        "Any");
        plugin.registerNode( "Interp", InterpNode::id,
        InterpNode::creator,
        InterpNode::initialize,
        MPxNode::kDependNode, &UserClassify);
        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj)
    {
        MFnPlugin plugin( obj );
        plugin.deregisterNode( InterpNode::id );
        return MS::kSuccess;
    }

initialize

    MStatus InterpNode::initialize()
    {
        MFnNumericAttribute nAttr;
        // Inputs and Attributes
        //
        // User defined attributes require a long-name and short-
        // name that are required to be unique within the node.
        // (See the compound attribute color1 named "Sides".)
        //
        // Rendering attributes that your node wants to get from
        // the sampler require them to be defined given the pre-
        // defined unique long-name.(See the compound attribute
        // aNormalCamera named "normalCamera".)
        //
        // User defined Attributes are generally something that you
        // want to store in the Maya file. The setStorable(true)
        // method enables an attribute to be stored into the Maya
        // scene file.
        //
        // Rendering attributes are primarily data that is
        // generated per sample and not something that you want to
        // store in a file. To disable an attribute from being
        // recorded to the Maya scene file use the
        // setStorable(false) method.
        //
        // Simple attributes that represent a range of values can
        // enable a slider on the Attribute Editor by using the
        // methods setMin() and setMax().
        // (See the simple attribute InputValue named "Power".)
        //
        // Compound attributes that represent a vector of 3 floats
        // can enable a color swatch on the Attribute Editor that
        // will launch a color picker tool by using the method
        // setUsedAsColor(true).
        // (See the compound attribute color1 name "Sides".)
        //
        // Both Simple and Compound attributes can be initialized
        // with a default value using the method setDefault().
        //
        // Attributes by default show up in the Attribute Editor
        // and in the Connection Editor unless they are specified
        // as being hidden by using the method setHidden(true).
        //
        // Attributes by default have both read/write access in the
        // dependency graph. To change an attributes behaviour you
        // can use the methods setReadable() and setWritable(). The
        // method setReadable(true) indicates that the attribute
        // can be used as the source in a dependency graph
        // connection. The method setWritable(true) indicates that
        // the attribute can be used as the destination in a
        // dependency graph connection.
        // (See the compound attribute aOutColor named "outColor"
        // below. It has been marked as a read-only attribute since
        // it is the computed result of the node, it is not stored
        // in the Maya file since it is always computed, and it is
        // marked as hidden to prevent it from being displayed in
        // the user interface.)
        //
        //
        // User defined input value
        InputValue = nAttr.create( "Power", "pow",
        MFnNumericData::kFloat);
        nAttr.setDefault(1.0f);
        nAttr.setMin(0.0f);
        nAttr.setMax(3.0f);
        nAttr.setStorable(true);
        // User defined color attribute
        color1R = nAttr.create( "color1R", "c1r",
        MFnNumericData::kFloat);
        color1G = nAttr.create( "color1G", "c1g",
        MFnNumericData::kFloat);
        color1B = nAttr.create( "color1B", "c1b",
        MFnNumericData::kFloat);
        color1 = nAttr.create( "Sides", "c1", color1R, color1G,
        color1B);
        nAttr.setStorable(true);
        nAttr.setUsedAsColor(true);
        nAttr.setDefault(1.0f, 1.0f, 1.0f);
        color2R = nAttr.create( "color2R", "c2r",
        MFnNumericData::kFloat);
        color2G = nAttr.create( "color2G", "c2g",
        MFnNumericData::kFloat);
        color2B = nAttr.create( "color2B", "c2b",
        MFnNumericData::kFloat);
        color2 = nAttr.create( "Facing", "c2", color2R,
        color2G, color2B);
        nAttr.setStorable(true);
        nAttr.setUsedAsColor(true);
        nAttr.setDefault(0.0f, 0.0f, 0.0f);
        // Surface Normal supplied by the render sampler
        aNormalCameraX = nAttr.create( "normalCameraX", "nx",
        MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setDefault(1.0f);
        aNormalCameraY = nAttr.create( "normalCameraY", "ny",
        MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setDefault(1.0f);
        aNormalCameraZ = nAttr.create( "normalCameraZ", "nz",
        MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setDefault(1.0f);
        aNormalCamera = nAttr.create( "normalCamera","n",
        aNormalCameraX,
        aNormalCameraY, aNormalCameraZ);
        nAttr.setStorable(false);
        nAttr.setHidden(true);
        // Point on surface in camera space, will be used to compute view vector
        aPointCameraX = nAttr.create( "pointCameraX", "px",
        MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setDefault(1.0f);
        aPointCameraY = nAttr.create( "pointCameraY", "py",
        MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setDefault(1.0f);
        aPointCameraZ = nAttr.create( "pointCameraZ", "pz",
        MFnNumericData::kFloat);
        nAttr.setStorable(false);
        nAttr.setDefault(1.0f);
        aPointCamera = nAttr.create( "pointCamera","p",
        aPointCameraX,
        aPointCameraY, aPointCameraZ);
        nAttr.setStorable(false);
        nAttr.setHidden(true);
        // Outputs
        aOutColorR = nAttr.create( "outColorR", "ocr",
        MFnNumericData::kFloat);
        aOutColorG = nAttr.create( "outColorG", "ocg",
        MFnNumericData::kFloat);
        aOutColorB = nAttr.create( "outColorB", "ocb",
        MFnNumericData::kFloat);
        aOutColor = nAttr.create( "outColor", "oc",
        aOutColorR, aOutColorG, aOutColorB);
        nAttr.setStorable(false);
        nAttr.setHidden(false);
        nAttr.setReadable(true);
        nAttr.setWritable(false);
        addAttribute(InputValue);
        addAttribute(color1R);
        addAttribute(color1G);
        addAttribute(color1B);
        addAttribute(color1);
        addAttribute(color2R);
        addAttribute(color2G);
        addAttribute(color2B);
        addAttribute(color2);
        addAttribute(aNormalCameraX);
        addAttribute(aNormalCameraY);
        addAttribute(aNormalCameraZ);
        addAttribute(aNormalCamera);
        addAttribute(aPointCameraX);
        addAttribute(aPointCameraY);
        addAttribute(aPointCameraZ);
        addAttribute(aPointCamera);
        addAttribute(aOutColorR);
        addAttribute(aOutColorG);
        addAttribute(aOutColorB);
        addAttribute(aOutColor);
        attributeAffects (InputValue, aOutColor);
        attributeAffects (color1R, color1);
        attributeAffects (color1G, color1);
        attributeAffects (color1B, color1);
        attributeAffects (color1, aOutColor);
        attributeAffects (color2R, color2);
        attributeAffects (color2G, color2);
        attributeAffects (color2B, color2);
        attributeAffects (color2, aOutColor);
        attributeAffects (aNormalCameraX, aOutColor);
        attributeAffects (aNormalCameraY, aOutColor);
        attributeAffects (aNormalCameraZ, aOutColor);
        attributeAffects (aNormalCamera, aOutColor);
        attributeAffects (aPointCameraX, aOutColor);
        attributeAffects (aPointCameraY, aOutColor);
        attributeAffects (aPointCameraZ, aOutColor);
        attributeAffects (aPointCamera, aOutColor);
        return MS::kSuccess;
    }

Id String

    MTypeId InterpNode::id( 0x81005 );

compute method

    MStatus InterpNode::compute( const MPlug& plug, MDataBlock& block )
    {
        int k=0;
        float gamma,scalar;
        k |= (plug == aOutColor);
        k |= (plug == aOutColorR);
        k |= (plug == aOutColorG);
        k |= (plug == aOutColorB);
        if (!k) return MS::kUnknownParameter;
        MFloatVector resultColor(0.0,0.0,0.0);
        MFloatVector& Side = block.inputValue( color1 ).
        asFloatVector();
        MFloatVector& Face = block.inputValue( color2 ).
        asFloatVector();
        MFloatVector& surfaceNormal = block.
        inputValue( aNormalCamera ).
        asFloatVector();
        MFloatVector& viewVector = block.
        inputValue( aPointCamera ).
        asFloatVector();
        float power = block.inputValue( InputValue ).asFloat();
        // Normalize the view vector
        double d = sqrt((viewVector[0] * viewVector[0]) +
        (viewVector[1] * viewVector[1]) +
        (viewVector[2] * viewVector[2]));
        if (d != (double)0.0) {
            viewVector[0] /= d;
            viewVector[1] /= d;
            viewVector[2] /= d;
        }

        // find dot product
        float scalarNormal = ((viewVector[0]*surfaceNormal[0])
                           + (viewVector[1]*surfaceNormal[1])
                           + (viewVector[2]*surfaceNormal[2]));
        // take the absolute value
        if (scalarNormal < 0.0) scalarNormal *= -1.0;
        // Use InputValue to change interpolation
        // power == 1.0 linear
        // power >= 0.0 use gamma function
        //
        if (power > 0.0) {
            gamma = 1.0 / power;
            scalar = pow(scalarNormal,gamma);
        }
        else { scalar = 0.0; }
        // Interpolate the colors
        MFloatVector interp(0.0,0.0,0.0);
        interp[0] = scalar * (Face[0] - Side[0]);
        interp[1] = scalar * (Face[1] - Side[1]);
        interp[2] = scalar * (Face[2] - Side[2]);
        resultColor[0] = Side[0] + interp[0];
        resultColor[1] = Side[1] + interp[1];
        resultColor[2] = Side[2] + interp[2];
        // set ouput color attribute
        MDataHandle outColorHandle = block.
        outputValue( aOutColor );
        MFloatVector& outColor = outColorHandle.
        asFloatVector();
        outColor = resultColor;
        outColorHandle.setClean();
        return MS::kSuccess;
    }

Attribute Editor view for InterpNode Example

Connection Editor view of an InterpNode connection

Hypergraph view of an InterpNode connection