|
/* |
|
An example of writing to an array plug |
|
|
|
Description |
|
----------------------------------------------------------- |
|
|
|
There are two ways of achieving this |
|
|
|
1. Based on input, dynamically generate plugs in an array |
|
2. Based on a connected array output, write to that |
|
|
|
This is an example of (2) the latter |
|
|
|
|
|
Usage: |
|
----------------------------------------------------------- |
|
from maya import cmds |
|
array = cmds.createNode("myArray") |
|
|
|
for i in range(5): |
|
emitter = cmds.createNode("transform", name="node%i" % i) |
|
cmds.connectAttr(array + ".values[%d]" % i, emitter + ".ty") |
|
|
|
assert cmds.getAttr(emitter + ".ty") == 4.0 |
|
*/ |
|
|
|
#include <maya/MPxNode.h> |
|
#include <maya/MTypeId.h> |
|
#include <maya/MString.h> |
|
#include <maya/MPlug.h> |
|
#include <maya/MDataBlock.h> |
|
#include <maya/MDataHandle.h> |
|
#include <maya/MArrayDataBuilder.h> |
|
#include <maya/MFnTypedAttribute.h> |
|
#include <maya/MFnNumericAttribute.h> |
|
#include <maya/MFnPlugin.h> |
|
#include <maya/MFnPluginData.h> |
|
#include "stdio.h" // cerr |
|
|
|
|
|
class ArrayPlug : public MPxNode { |
|
public: |
|
ArrayPlug(); |
|
|
|
MStatus compute(const MPlug &plug, MDataBlock &dataBlock) override; |
|
MStatus computeValues(const MPlug &plug, MDataBlock &dataBlock); |
|
|
|
static void* creator() { return new ArrayPlug; } |
|
|
|
static MStatus initialize(); |
|
static const MTypeId id; |
|
static const MString typeName; |
|
|
|
// Attributes |
|
static MObject values; |
|
}; |
|
|
|
|
|
const MTypeId ArrayPlug::id(0x85003); |
|
const MString ArrayPlug::typeName("myArray"); |
|
|
|
MObject ArrayPlug::values; |
|
|
|
inline void SOFTCHECK(MStatus status, MString msg) { |
|
if (!status) { cerr << "ERROR: " << msg << "\n"; } |
|
} |
|
|
|
#define HARDCHECK(STAT, MSG) \ |
|
if (MS::kSuccess != STAT) { \ |
|
cerr << "ERROR: " << MSG << endl; \ |
|
return MS::kFailure; \ |
|
} |
|
|
|
MStatus ArrayPlug::initialize() { |
|
MStatus status; |
|
|
|
MFnTypedAttribute typFn; |
|
MFnNumericAttribute numFn; |
|
|
|
// Each output will have a value corresponding to its logical index |
|
values = numFn.create("values", "v", MFnNumericData::kFloat, 0.0f, &status); |
|
numFn.setArray(true); |
|
numFn.setStorable(false); |
|
numFn.setReadable(true); |
|
numFn.setWritable(false); |
|
|
|
// NOTE: This is what enables us to use MArrayDataBuilder |
|
// in computeValues() below |
|
numFn.setUsesArrayDataBuilder(true); |
|
|
|
SOFTCHECK(status, "failed to create values"); |
|
|
|
addAttribute(values); |
|
|
|
return MStatus::kSuccess; |
|
} |
|
|
|
|
|
ArrayPlug::ArrayPlug() {} |
|
|
|
|
|
/** |
|
* @brief Output the index or each array plug as its value |
|
* |
|
* _____________ |
|
* | | |
|
* | values o |
|
* | values[0]-| o---> 0.0 |
|
* | values[1]-| o---> 1.0 |
|
* | values[2]-| o---> 2.0 |
|
* | values[3]-| o |
|
* | | |
|
* | | |
|
* |_____________| |
|
* |
|
* |
|
*/ |
|
MStatus ArrayPlug::computeValues(const MPlug& plug, MDataBlock& datablock) { |
|
MStatus status { MS::kSuccess }; |
|
|
|
int index = plug.logicalIndex(&status); |
|
HARDCHECK(status, "Could not get logical index"); |
|
|
|
// NOTE: Rather than calling `.outputValue` |
|
MArrayDataHandle arrayhandle = datablock.outputArrayValue(values, &status); |
|
HARDCHECK(status, "computeValues : outputArrayValue"); |
|
|
|
// NOTE: An array data handle has a different interface than interacting |
|
// with a MDataHandle directly. We're essentially fetching a |
|
// handle for a given index in the array. Maya calls this "builder" |
|
// presumably because you can also *create* indices in this array |
|
// rather than merely writing to them |
|
MArrayDataBuilder builder = arrayhandle.builder(&status); |
|
HARDCHECK(status, "computeValues : builder"); |
|
|
|
// NOTE: Even though we're calling *add*Element we aren't actually |
|
// adding an element. You can think of this as the []-syntax |
|
// of a C-array, i.e. arrayhandle[index]; |
|
MDataHandle datahandle = builder.addElement(index, &status); |
|
HARDCHECK(status, "computeValues : addElement"); |
|
|
|
// Just to highlight what the value actually is |
|
int value = index; |
|
|
|
datahandle.setFloat(value); |
|
datablock.setClean(plug); |
|
|
|
return status; |
|
} |
|
|
|
|
|
|
|
MStatus ArrayPlug::compute(const MPlug &plug, MDataBlock &datablock) { |
|
MStatus status { MS::kSuccess }; |
|
|
|
if (plug == values) { |
|
return computeValues(plug, datablock); |
|
} |
|
|
|
else { |
|
status = MS::kUnknownParameter; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
|
|
MStatus initializePlugin (MObject obj) { |
|
MStatus status; |
|
MFnPlugin plugin(obj, "ArrayPlug", "2020.2", "Any"); |
|
|
|
status = plugin.registerNode(ArrayPlug::typeName, |
|
ArrayPlug::id, |
|
ArrayPlug::creator, |
|
ArrayPlug::initialize); |
|
|
|
if (!status) { |
|
status.perror("registerNode"); |
|
return status; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
MStatus uninitializePlugin(MObject obj) |
|
{ |
|
MStatus status; |
|
MFnPlugin plugin(obj); |
|
|
|
status = plugin.deregisterNode(ArrayPlug::id); |
|
|
|
if (!status) { |
|
status.perror("deregisterNode"); |
|
return status; |
|
} |
|
|
|
return status; |
|
} |