// ==========================================================================
// 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.
// ==========================================================================
// Produces the dependency graph node "multiCurve".
// This plug-in demonstrates how to use the MArrayDataBuilder class to
// create an array attribute in a compute function, the number of elements
// of which change on each compute cycle.
// The node accepts a "nurbsCurve" as input and outputs an array of "nurbsCurves" as outputs.
// The number of curves is controlled by the numCurves attribute and the spacing between
// each of the output curves is controlled by the curveOffset attribute.
// Both "numCurves" and "curveOffset" are keyable.
// To use this plug-in:
// (1) Load it and then execute the MEL command "source multiCurveNode.mel".
// This will create a curve, hook it up to an instance of a multiCurve node,
// keyframe the numCurves and curveOffset attributes, and then hook the output
// of the multiCurve node to a curveVarGroup node that will display all the
// output curves.
// (2) Once this script has been run, push play and as the animation progresses,
// new curves will be created and the spacing between them will increase.
#include <string.h>
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MPxNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MFnNurbsCurveData.h>
#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MPointArray.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MArrayDataHandle.h>
#include <maya/MArrayDataBuilder.h>
class multiCurve : public MPxNode
multiCurve() {};
virtual ~multiCurve();
virtual MStatus compute( const MPlug& plug, MDataBlock& data );
static void* creator();
static MStatus initialize();
static MObject inputCurve;
static MObject outputCurves;
static MObject numCurves;
static MObject curveOffset;
static MTypeId id;
MTypeId multiCurve::id( 0x8000a );
MObject multiCurve::inputCurve;
MObject multiCurve::outputCurves;
MObject multiCurve::numCurves;
MObject multiCurve::curveOffset;
// Error macro: if not successful, print error message and return
// the MStatus instance containing the error code.
// Assumes that "stat" contains the error value
#define PERRORfail(stat,msg) \
if (!(stat)) { \
stat.perror((msg)); \
return (stat); \
multiCurve::~multiCurve () {}
void* multiCurve::creator()
return new multiCurve();
MStatus multiCurve::initialize()
MStatus stat;
MFnTypedAttribute typedAttr;
numCurves = nAttr.create ("numCurves", "nc",
PERRORfail(stat, "initialize create numCurves attribute");
CHECK_MSTATUS ( nAttr.setKeyable( true ) );
stat = addAttribute( numCurves );
PERRORfail(stat, "addAttribute(numCurves)");
curveOffset = nAttr.create ("curveOffset", "co",
PERRORfail(stat, "initialize create curveOffset attribute");
CHECK_MSTATUS ( nAttr.setKeyable( true ) );
stat = addAttribute( curveOffset );
PERRORfail(stat, "addAttribute(curveOffset)");
inputCurve = typedAttr.create( "inputCurve", "ic",
PERRORfail(stat, "initialize create inputCurve attribute");
CHECK_MSTATUS ( typedAttr.setReadable( false ) );
CHECK_MSTATUS ( typedAttr.setWritable( true ) );
stat = addAttribute( inputCurve );
PERRORfail(stat, "addAttribute(inputCurve)");
outputCurves = typedAttr.create( "outputCurves", "oc",
PERRORfail(stat, "initialize create outputCurves attribute");
CHECK_MSTATUS ( typedAttr.setArray( true ) );
CHECK_MSTATUS ( typedAttr.setReadable( true ) );
CHECK_MSTATUS ( typedAttr.setWritable( false ) );
CHECK_MSTATUS ( typedAttr.setUsesArrayDataBuilder( true ) );
stat = addAttribute( outputCurves );
PERRORfail(stat, "addAttribute(outputCurves)");
stat = attributeAffects( numCurves, outputCurves );
PERRORfail(stat, "attributeAffects(inputCurve, outputCurves)");
stat = attributeAffects( curveOffset, outputCurves );
PERRORfail(stat, "attributeAffects(inputCurve, outputCurves)");
stat = attributeAffects( inputCurve, outputCurves );
PERRORfail(stat, "attributeAffects(inputCurve, outputCurves)");
return stat;
MStatus multiCurve::compute( const MPlug& plug, MDataBlock& data )
MStatus stat;
if ( plug == outputCurves )
MDataHandle numCurvesHandle = data.inputValue(numCurves, &stat);
PERRORfail(stat, "multiCurve::compute getting numCurves");
int num = numCurvesHandle.asLong();
MDataHandle curveOffsetHandle = data.inputValue(curveOffset, &stat);
PERRORfail(stat, "multiCurve::compute getting curveOffset");
double baseOffset = curveOffsetHandle.asDouble();
MDataHandle inputCurveHandle = data.inputValue(inputCurve, &stat);
PERRORfail(stat, "multiCurve::compute getting inputCurve");
MObject inputCurveObject ( inputCurveHandle.asNurbsCurveTransformed() );
MFnNurbsCurve inCurveFS ( inputCurveObject );
MArrayDataHandle outputArray = data.outputArrayValue(outputCurves,
PERRORfail(stat, "multiCurve::compute getting output data handle");
// Create an array data build that is preallocated to hold just
// the number of curves we plan on creating. When this builder
// is set in to the MArrayDataHandle at the end of the compute
// method, the new array will replace the existing array in the
// scene.
// If the number of elements of the multi does not change between
// compute cycles, then one can reuse the space allocated on a
// previous cycle by extracting the existing builder from the
// MArrayDataHandle:
// MArrayDataBuilder builder( outputArray.builder(&stat) );
// this later form of the builder will allow you to rewrite elements
// of the array, and to grow it, but the array can only be shrunk by
// explicitly removing elements with the method
// MArrayDataBuilder::removeElement(unsigned index);
MArrayDataBuilder builder(outputCurves, num, &stat);
PERRORfail(stat, "multiCurve::compute creating builder");
for (int curveNum = 0; curveNum < num; curveNum++) {
MDataHandle outHandle = builder.addElement(curveNum);
MFnNurbsCurveData dataCreator;
MObject outCurveData = dataCreator.create();
MObject outputCurve = inCurveFS.copy(inputCurveObject,
outCurveData, &stat);
PERRORfail(stat, "multiCurve::compute copying curve");
MFnNurbsCurve outCurveFS ( outputCurve );
double offset = baseOffset * (curveNum+1);
outCurveFS.getCVs ( cvs, MSpace::kWorld );
int numCVs = cvs.length();
for (int i = 0; i < numCVs; i++) {
cvs[i].x += offset;
outCurveFS.setCVs ( cvs );
// Set the builder back into the output array. This statement
// is always required, no matter what constructor was used to
// create the builder.
stat = outputArray.set(builder);
PERRORfail(stat, "multiCurve::compute setting the builder");
// Since we compute all the elements of the array, instead of
// just marking the plug we were asked to compute as clean, mark
// every element of the array as clean to prevent further calls
// to this compute method during this DG evaluation cycle.
stat = outputArray.setAllClean();
PERRORfail(stat, "multiCurve::compute cleaning outputCurves");
} else {
return stat;
MStatus initializePlugin( MObject obj )
MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");
CHECK_MSTATUS (plugin.registerNode( "multiCurve", multiCurve::id,
multiCurve::creator, multiCurve::initialize ) );
return MS::kSuccess;;
MStatus uninitializePlugin( MObject obj )
MFnPlugin plugin( obj );
CHECK_MSTATUS (plugin.deregisterNode( multiCurve::id ) );
return MS::kSuccess;;