Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active January 21, 2024 13:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mottosso/ff10a18ea6223a8c382012f1c1d67bad to your computer and use it in GitHub Desktop.
Save mottosso/ff10a18ea6223a8c382012f1c1d67bad to your computer and use it in GitHub Desktop.
Maya API Example - Writing to an Array Plug

Maya API Example - Writing to an Array Plug

Here's an example of how to write to an array plug. It's a little different than your normal plugs, and the examples make it real challenging to figure out what's going on.

Test

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

Build

git clone https://gist.github.com/ff10a18ea6223a8c382012f1c1d67bad.git ArrayPlug
cd ArrayPlug
mkdir build
cd build
export DEVKIT_LOCATION=/path/to/devkit
cmake .. -GNinja
cmake --build .
/*
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;
}
cmake_minimum_required(VERSION 2.8)
include($ENV{DEVKIT_LOCATION}/cmake/pluginEntry.cmake)
set(PROJECT_NAME ArrayPlug)
set(SOURCE_FILES ArrayPlug.cpp)
set(LIBRARIES
OpenMaya
OpenMayaFX
Foundation
)
build_plugin()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment