affectsNode.cpp

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#include <maya/MIOStream.h>
#include <string.h>
#include <math.h>

#include <maya/MPxNode.h> 

#include <maya/MFnNumericAttribute.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnPlugin.h>

#include <maya/MString.h> 
#include <maya/MTypeId.h> 
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MVector.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
 
// Plugin Affects Class        //

// INTRODUCTION:
//  This class will create an "affects" node. This node is used for
//  demonstrating attributeAffects relationships involving dynamic
//  attributes.
//
// WHAT THIS PLUG-IN DEMONSTRATES:
//  This plug-in creates a node called "affects". Add two dynamic
//  integer attributes called "A" and "B". When you change the value on
//  A, note that B will recompute.
//
// HOW TO USE THIS PLUG-IN:
//  (1) Compile the plug-in
//  (2) Load the compiled plug-in into Maya via the plug-in manager
//  (3) Create an "affects" node by typing the MEL command:
//          createNode affects;
//  (4) Add two integer dynamic attributes to the newly created
//      affects node by typing the MEL command:
//          addAttr -ln A -at long  affects1;
//          addAttr -ln B -at long  affects1;
//  (5) Change the value of "A" to 10 by typing the MEL command:
//          setAttr affects1.A 10;
//      At this point, the affectsNode::setDependentsDirty() method
//      gets called which causes "B" to be marked dirty.
//  (6) Compute the value on "B" by doing a getAttr:
//          getAttr affects1.B;
//      The affectsNode::compute() method is entered which copies the
//      value from "A" (i.e. 10) to "B".
//
class affects : public MPxNode
{
public:
                        affects();
    virtual             ~affects(); 

    virtual MStatus     compute( const MPlug& plug, MDataBlock& data );
    virtual MStatus     setDependentsDirty( const MPlug& plugBeingDirtied,
                                MPlugArray &affectedPlugs );

    static  void*       creator();
    static  MStatus     initialize();

    static  MTypeId     id;             // The IFF type id
};

// IFF type ID
// Each node requires a unique identifier which is used by
// MFnDependencyNode::create() to identify which node to create, and by
// the Maya file format.
//
// For local testing of nodes you can use any identifier between
// 0x00000000 and 0x0007ffff, but for any node that you plan to use for
// more permanent purposes, you should get a universally unique id from
// Autodesk Support. You will be assigned a unique range that you can manage
// on your own.
//
MTypeId affects::id( 0x80028 );

// This node does not need to perform any special actions on creation or
// destruction
//
affects::affects() {}
affects::~affects() {}

// The compute() method does the actual work of the node using the inputs
// of the node to generate its output.
//
// Compute takes two parameters: plug and data.
// - Plug is the the data value that needs to be recomputed
// - Data provides handles to all of the nodes attributes, only these
//   handles should be used when performing computations.
//
MStatus affects::compute( const MPlug& plug, MDataBlock& data )
{
    MStatus status;
    MObject thisNode = thisMObject();
    MFnDependencyNode fnThisNode( thisNode );
    fprintf(stderr,"affects::compute(), plug being computed is \"%s\"\n",
            plug.name().asChar());
 
    if ( plug.partialName() == "B" ) {
        // Plug "B" is being computed. Assign it the value on plug "A"
        // if "A" exists.
        //
        MPlug pA = fnThisNode.findPlug( "A", &status );
        if ( MStatus::kSuccess == status ) {
            fprintf(stderr,"\t\t... found dynamic attribute \"A\", copying its value to \"B\"\n");
            MDataHandle inputData = data.inputValue( pA, &status );
            CHECK_MSTATUS( status );
            int value = inputData.asInt();

            MDataHandle outputHandle = data.outputValue( plug );
            outputHandle.set( value );
            data.setClean(plug);
        }
    } else {
        return MS::kUnknownParameter;
    }
    return( MS::kSuccess );
}

// The creator() method allows Maya to instantiate instances of this node.
// It is called every time a new instance of the node is requested by
// either the createNode command or the MFnDependencyNode::create()
// method.
//
// In this case creator simply returns a new affects object.
//
void* affects::creator()
{
    return( new affects() );
}

// The initialize method is called only once when the node is first
// registered with Maya. In general,
//
MStatus affects::initialize()
{
    return( MS::kSuccess );
}

// The setDependentsDirty() method allows attributeAffects relationships
// in a much more general way than via MPxNode::attributeAffects
// which is limited to static attributes only.
// The setDependentsDirty() method allows relationships to be established
// between any combination of dynamic and static attributes.
//
// Within a setDependentsDirty() implementation you get passed in the
// plug which is being set dirty, and then, based upon which plug it is,
// you may choose to dirty any other plugs by adding them to the
// affectedPlugs list.
//
// In almost all cases, the relationships you set up will be fixed for
// the duration of Maya, such as "A affects B". However, you can also
// set up relationships which depend upon some external factor, such
// as the current frame number, the time of day, if maya was invoked in
// batch mode, etc. These sorts of relationships are straightforward to
// implement in your setDependentsDirty() method.
//
// There may also be situations where you need to look at values in the
// dependency graph. It is VERY IMPORTANT that when accessing DG values
// you do not cause a DG evaluation. This is because your setDependentsDirty()
// method is called during dirty processing and causing an evalutaion could
// put Maya into an infinite loop. The only safe way to look at values
// on plugs is via the MDataBlock::outputValue() which does not trigger
// an evaluation. It is recommeneded that you only look at plugs whose
// values are constant or you know have already been computed.
//
// For this example routine, we will only implement the simplest case
// of a relationship.
//
MStatus affects::setDependentsDirty( const MPlug &plugBeingDirtied,
        MPlugArray &affectedPlugs )
{
    MStatus status;
    MObject thisNode = thisMObject();
    MFnDependencyNode fnThisNode( thisNode );

    if ( plugBeingDirtied.partialName() == "A" ) {
        // "A" is dirty, so mark "B" dirty if "B" exists.
        // This implements the relationship "A affects B".
        //
        fprintf(stderr,"affects::setDependentsDirty, \"A\" being dirtied\n");
        MPlug pB = fnThisNode.findPlug( "B", &status );
        if ( MStatus::kSuccess == status ) {
            fprintf(stderr,"\t\t... dirtying \"B\"\n");
            CHECK_MSTATUS( affectedPlugs.append( pB ) );
        }
    }
    return( MS::kSuccess );
}

// These methods load and unload the plugin, registerNode registers the
// new node type with maya
//
MStatus initializePlugin( MObject obj )
{ 
    MStatus   status;
    MFnPlugin plugin( obj, PLUGIN_COMPANY , "6.0", "Any");

    status = plugin.registerNode( "affects", affects::id, affects::creator,
                                  affects::initialize );
    if (!status) {
        status.perror("registerNode");
        return( status );
    }

    return( status );
}

MStatus uninitializePlugin( MObject obj)
{
    MStatus   status;
    MFnPlugin plugin( obj );

    status = plugin.deregisterNode( affects::id );
    if (!status) {
        status.perror("deregisterNode");
        return( status );
    }

    return( status );
}