stringFormatNode/stringFormatNode.cpp

stringFormatNode/stringFormatNode.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: stringFormatNode.cpp
//
// This plug-in is an example of a user-defined dependency graph node.
// It takes several numbers as input, as well as a format string. It
// generates a formatted string as an output.
//
// All occurences of ^[0-9][dft] are replaced with the corresponding
// input parameter using the specified format.
//
// d means output as an integer.
// f means output as a floating point value.
// t means output as a timecode.
//
// For example, the string "format ^1f ^2d ^0t end format", using the
// array {2.3, 3.4, 4.5} as an input will generate the result
// "format 3.4 4 00:00:02 end format"
//
#include <math.h>
#include <maya/MIOStream.h>
#include <maya/MPxNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnStringData.h>
#include <maya/MFnPlugin.h>
// The stringFormat class defines the attributes and methods necessary
// for the stringFormatNode plugin
//
class stringFormat : public MPxNode
{
public:
stringFormat();
virtual ~stringFormat();
virtual MStatus compute( const MPlug& plug, MDataBlock& data );
static void* creator();
static MStatus initialize();
public:
static MTypeId id; // Unique node Id
static MObject attrFormat; // Formatting string
static MObject attrValues; // Input values (array)
static MObject attrOutput; // Result
};
MTypeId stringFormat::id( 0x81034 );
MObject stringFormat::attrFormat;
MObject stringFormat::attrValues;
MObject stringFormat::attrOutput;
// The creator method creates an instance of the stringFormat class
// and is the first method called by Maya when a stringFormat node
// needs to be created.
//
void* stringFormat::creator()
{
return new stringFormat();
}
// 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
// value.
//
MStatus stringFormat::initialize()
{
MFnTypedAttribute typedAttr;
MFnStringData stringData;
MStatus stat;
MStatus stat2;
// Setup the input attributes
//
attrFormat = typedAttr.create("format", "f", MFnData::kString,
stringData.create(&stat2), &stat);
CHECK_MSTATUS( stat2 );
CHECK_MSTATUS( stat );
CHECK_MSTATUS( typedAttr.setStorable( true ) );
CHECK_MSTATUS( typedAttr.setKeyable( true ) );
attrValues = numAttr.create("values", "v", MFnNumericData::kDouble,
0, &stat);
CHECK_MSTATUS( stat );
CHECK_MSTATUS( numAttr.setArray( true ) );
CHECK_MSTATUS( numAttr.setReadable( false ) );
CHECK_MSTATUS( numAttr.setIndexMatters( true ) );
CHECK_MSTATUS( numAttr.setStorable( true ) );
CHECK_MSTATUS( numAttr.setKeyable( true ) );
attrOutput = typedAttr.create( "output", "o", MFnData::kString,
stringData.create(&stat2), &stat);
CHECK_MSTATUS( stat2 );
CHECK_MSTATUS( stat );
CHECK_MSTATUS( typedAttr.setWritable( false ) );
CHECK_MSTATUS( typedAttr.setStorable( false ) );
// Add the attributes to the node
//
CHECK_MSTATUS( addAttribute( attrFormat ) );
CHECK_MSTATUS( addAttribute( attrValues ) );
CHECK_MSTATUS( addAttribute( attrOutput ) );
// Set the attribute dependencies
//
CHECK_MSTATUS( attributeAffects( attrFormat, attrOutput ) );
CHECK_MSTATUS( attributeAffects( attrValues, attrOutput ) );
return MS::kSuccess;
}
// The constructor does nothing
//
stringFormat::stringFormat() {}
// The destructor does nothing
//
stringFormat::~stringFormat() {}
// 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.
//
// Look for ^[0-9][a-z] after the given index. Returns the position of
// the characted just after '^'
int findNextMatch(MString & string, int indx, int & param, char & letter)
{
// Warning this is not I18N compliant
const char * str = string.asChar();
while (str[indx]) {
if ((str[indx] == '^') &&
(str[indx+1] >= '0') && (str[indx+1] <= '9') &&
(str[indx+2] >= 'a') && (str[indx+1] <= 'z')) {
param = str[indx+1] - '0';
letter = str[indx+2];
return indx+1;
}
indx++;
}
return -1;
}
MStatus stringFormat::compute (const MPlug& plug, MDataBlock& data)
{
MStatus status;
// Check that the requested recompute is one of the output values
//
if (plug == attrOutput) {
// Read the input values
//
MDataHandle inputData = data.inputValue (attrFormat, &status);
CHECK_MSTATUS( status );
MString format = inputData.asString();
// Get input data handle, use outputArrayValue since we do not
// want to evaluate all inputs, only the ones related to the
// requested multiIndex. This is for efficiency reasons.
//
MArrayDataHandle vals = data.outputArrayValue(attrValues, &status);
CHECK_MSTATUS( status );
int indx = 0;
int param;
char letter;
while ((indx = findNextMatch(format, indx, param, letter)) > 0) {
double val = 0.;
status = vals.jumpToElement(param);
if (status == MStatus::kSuccess) {
MDataHandle thisVal = vals.inputValue( &status );
if (status == MStatus::kSuccess) {
val = thisVal.asDouble();
}
}
MString replace;
bool valid = false;
switch (letter) {
case 'd': // Integer
val = floor(val+.5);
// No break here
case 'f': // Float
replace.set(val);
valid = true;
break;
case 't': // Timecode
{
const char * sign = "";
if (val<0) {
sign = "-";
val = -val;
}
int valInt = (int)(val+.5);
int sec = valInt / 24;
int frame = valInt - sec * 24;
int min = sec / 60;
sec -= min * 60;
int hour = min / 60;
min -= hour * 60;
char buffer[90];
if (hour>0)
sprintf(buffer, "%s%d:%02d:%02d.%02d",
sign, hour, min, sec, frame);
else
sprintf(buffer, "%s%02d:%02d.%02d",
sign, min, sec, frame);
replace = buffer;
}
valid = true;
break;
}
if (valid) {
format = format.substring(0, indx-2) +
replace + format.substring(indx+2, format.length()-1);
indx += replace.length() - 3;
}
}
// Store the result
//
MDataHandle output = data.outputValue(attrOutput, &status );
CHECK_MSTATUS( status );
output.set( format );
} else {
}
return MS::kSuccess;
}
// The initializePlugin method is called by Maya when the plugin is
// loaded. It registers the node and the UI.
//
MStatus initializePlugin ( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj, PLUGIN_COMPANY, "8.0", "Any");
// Register the node
plugin.registerNode( "stringFormat", stringFormat::id,
stringFormat::creator, stringFormat::initialize));
// Create the UI for the plugin.
// Keep on going, do not fail the load because of this.
CHECK_MSTATUS(plugin.registerUI("stringFormatCreateUI",
"stringFormatDeleteUI", "", ""));
return status;
}
// The unitializePlugin is called when Maya needs to unload the plugin.
// It does the opposite of initialize.
//
MStatus uninitializePlugin( MObject obj)
{
MStatus status;
MFnPlugin plugin( obj );
CHECK_MSTATUS_AND_RETURN_IT(plugin.deregisterNode( stringFormat::id ));
return status;
}