circleNode/circleNode.cpp
 
 
 
circleNode/circleNode.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.
// ==========================================================================
//+

// Example Plugin: circleNode.cpp
//
// This plug-in is an example of a user-defined dependency graph node.
// It takes a number as input (such as time) and generates two output
// numbers one which describes a sine curve as the input varies and
// one that generates a cosine curve. If these two are hooked up to
// the x and z translation attributes of an object the object will describe
// move through a circle in the xz plane as time is changed.
//
// Executing the command "source circleNode" will run the MEL script which will
// create a new "Circle" menu with a single item. Selecting this will build
// a simple model (a sphere which follows a circular path) which can be played back,
// by clicking on the "play" icon on the time slider.  Note: the circleNode
// plugin needs to be loaded before the "Circle" menu item can be executed
// properly.
//
// The node has two additional attributes which can be changed to affect
// the animation, "scale" which defines the size of the circular path, and
// "frames" which defines the number of frames required for a complete circuit
// of the path. Either of these can be hooked up to other nodes, or can
// be simply set via the MEL command "setAttr" operating on the circle node
// "circleNode1" created by the MEL script. For example, "setAttr circleNode1.scale #"
// will change the size of the circle and "setAttr circleNode1.frames #"
// will cause objects to complete a circle in indicated number of frames.


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

#include <maya/MPxNode.h> 

#include <maya/MString.h> 
#include <maya/MTypeId.h> 
#include <maya/MPlug.h>

#include <maya/MFnNumericAttribute.h>
#include <maya/MVector.h>

#include <maya/MFnPlugin.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>

#include <math.h>

// The circle class defines the attributes
// and methods necessary for the circleNode plugin
//
class circle : public MPxNode
{
public:
                                                circle();
        virtual                         ~circle(); 

        virtual MStatus         compute( const MPlug& plug, MDataBlock& data );

        static  void*           creator();
        static  MStatus         initialize();
 
public:
        static  MObject         input;          // The input value.
        static  MObject         sOutput;        // The sinusoidal output value.
        static  MObject         cOutput;        // The cosinusoidal output value.
        static  MObject         frames;         // Number of frames for one circle.
        static  MObject         scale;          // Size of circle.
        static  MTypeId         id;
};

MTypeId     circle::id( 0x80005 );
MObject     circle::input;        
MObject     circle::sOutput;       
MObject     circle::cOutput;       
MObject     circle::frames;
MObject         circle::scale;

// The creator method creates an instance of the circleNode class
// and is the first method called by Maya
// when a circleNode needs to be created.
//
void* circle::creator()
{
        return new circle();
}

// The initialize routine is called after the node has been created.
// It sets up the input and output attributes and adds them to the node.
// Finally the dependencies are arranged so that when the inputs
// change Maya knowns to call compute to recalculate the output values.
// The inputs are: input, scale, frames
// The outputs are: sineOutput, cosineOutput
//
MStatus circle::initialize()
{
        MFnNumericAttribute nAttr;
        MStatus                         stat;

        // Setup the input attributes
        //
        input = nAttr.create( "input", "in", MFnNumericData::kFloat, 0.0,
                        &stat );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( nAttr.setStorable( true ) );

        scale = nAttr.create( "scale", "sc", MFnNumericData::kFloat, 10.0,
                        &stat );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( nAttr.setStorable( true ) );

        frames = nAttr.create( "frames", "fr", MFnNumericData::kFloat, 48.0,
                        &stat );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( nAttr.setStorable( true ) );

        // Setup the output attributes
        //
        sOutput = nAttr.create( "sineOutput", "so", MFnNumericData::kFloat,
                        0.0, &stat );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( nAttr.setWritable( false ) );
        CHECK_MSTATUS( nAttr.setStorable( false ) );

        cOutput = nAttr.create( "cosineOutput", "co", MFnNumericData::kFloat,
                        0.0, &stat );
        CHECK_MSTATUS( stat );
        CHECK_MSTATUS( nAttr.setWritable( false ) );
        CHECK_MSTATUS( nAttr.setStorable( false ) );

        // Add the attributes to the node
        //
        CHECK_MSTATUS( addAttribute( input ) );
        CHECK_MSTATUS( addAttribute( scale ) );
        CHECK_MSTATUS( addAttribute( frames ) );
        CHECK_MSTATUS( addAttribute( sOutput ) );
        CHECK_MSTATUS( addAttribute( cOutput ) );

        // Set the attribute dependencies
        //
        CHECK_MSTATUS( attributeAffects( input, sOutput ) );
        CHECK_MSTATUS( attributeAffects( input, cOutput ) );
        CHECK_MSTATUS( attributeAffects( scale, sOutput ) );
        CHECK_MSTATUS( attributeAffects( scale, cOutput ) );
        CHECK_MSTATUS( attributeAffects( frames, sOutput ) );
        CHECK_MSTATUS( attributeAffects( frames, cOutput ) );

        return MS::kSuccess;
} 

// The constructor does nothing
//
circle::circle() {}

// The destructor does nothing
//
circle::~circle() {}

// The compute method is called by Maya when the input values
// change and the output values need to be recomputed.
// The input values are read then using sinf() and cosf()
// the output values are stored on the output plugs.
//
MStatus circle::compute (const MPlug& plug, MDataBlock& data)
{
        
        MStatus returnStatus;
 
        // Check that the requested recompute is one of the output values
        //
        if (plug == sOutput || plug == cOutput) {
                // Read the input values
                //
                MDataHandle inputData = data.inputValue (input, &returnStatus);
                CHECK_MSTATUS( returnStatus );
                MDataHandle scaleData = data.inputValue (scale, &returnStatus);
                CHECK_MSTATUS( returnStatus );
                MDataHandle framesData = data.inputValue (frames, &returnStatus);
                CHECK_MSTATUS( returnStatus );

                // Compute the output values
                //
                float currentFrame = inputData.asFloat();
                float scaleFactor  = scaleData.asFloat();
                float framesPerCircle = framesData.asFloat();
                float angle = 6.2831853f * (currentFrame/framesPerCircle);
                float sinResult = sinf (angle) * scaleFactor;
                float cosResult = cosf (angle) * scaleFactor;

                // Store them on the output plugs
                //
                MDataHandle sinHandle = data.outputValue( circle::sOutput,
                                &returnStatus );
                CHECK_MSTATUS( returnStatus );
                MDataHandle cosHandle = data.outputValue( circle::cOutput,
                                &returnStatus );
                CHECK_MSTATUS( returnStatus );
                sinHandle.set( sinResult );
                cosHandle.set( cosResult );
                CHECK_MSTATUS( data.setClean( plug ) );
        } else {
                return MS::kUnknownParameter;
        }

        return MS::kSuccess;
}

// The initializePlugin method is called by Maya when the circleNode
// plugin is loaded.  It registers the circleNode which provides
// Maya with the creator and initialize methods to be called when
// a circleNode is created.
//
MStatus initializePlugin ( MObject obj )
{ 
        MStatus   status;
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");

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

        return status;
}

// The unitializePlugin is called when Maya needs to unload the plugin.
// It basically does the opposite of initialize by calling
// the deregisterCommand to remove it.
//
MStatus uninitializePlugin( MObject obj)
{
        MStatus   status;
        MFnPlugin plugin( obj );

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

        return status;
}