C++ API Reference: 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();
~stringFormat() override;
MStatus compute(const MPlug &plug, MDataBlock &data) override;
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(typedAttr.setStorable(true));
CHECK_MSTATUS(typedAttr.setKeyable(true));
attrValues = numAttr.create("values", "v", MFnNumericData::kDouble,
0, &stat);
CHECK_MSTATUS(numAttr.setArray(true));
CHECK_MSTATUS(numAttr.setReadable(false));
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(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::kUnknownParameter;
}
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;
}