Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active February 10, 2021 14:30
Show Gist options
  • Save mottosso/bc99f4d61782dee4b86c270367652614 to your computer and use it in GitHub Desktop.
Save mottosso/bc99f4d61782dee4b86c270367652614 to your computer and use it in GitHub Desktop.
Editable Initial State - Attempt 1

⬅️ Back

In this attempt, I'll tell outputTranslate to dirty whenever currentTime changes. Then, if currentTime == startTime then I can datablock.inputValue(aInWorldMatrix) but not actually write to outputTranslate.

const bool isStartTime = datablock.inputValue(aStartTime).asTime() ==
                         datablock.inputValue(aCurrentTime).asTime();

if (plug == aOutputTranslate) {
    if (isStartTime) datablock.inputValue(aInWorldMatrix).asMatrix();
    else             datablock.outputValue(aOutputTranslate);
}

This doesn't work, and doesn't really make sense. On the start frame, we want to leverage the fact that the outputTranslate attributes are passive and that the user can modify these even though they are connected. The modification isn't really important to us. Maya will draw the object wherever the user puts it and we can just sit back and relax. What I'd like to do however is record the position it ends up at.

But this isn't the way. Because initialState.outputTranslate plugs into pCube1.translate, and pCube1.worldMatrix plugs into initialState.inWorldMatrix. We can't query inWorldMatrix when outputting outputTranslate, that's a cycle.

// Warning: Cycle on 'initialStateNode1.outputTranslate' may not evaluate as expected.

attempt1


Build & Run

From Powershell.

cd c:\
git clone https://gist.github.com/bc99f4d61782dee4b86c270367652614.git initialStateNode
mkdir initialStateNode/build
cd initialStateNode/build
$env:DEVKIT_LOCATION="c:\path\to\your\devkit"
cmake .. -G Ninja
ninja

Then from the Script Editor.

import os
from maya import cmds

fname = r"c:\initialState\build\initialStateNode.mll"
cmds.loadPlugin(fname)

time = "time1"
cube, _ = cmds.polyCube()
cmds.move(0, 2, 0)
node = cmds.createNode("initialStateNode", parent=cube)
cmds.connectAttr(cube + ".worldMatrix[0]", node + ".inWorldMatrix")
cmds.connectAttr(node + ".outputTranslateX", cube + ".translateX")
cmds.connectAttr(node + ".outputTranslateY", cube + ".translateY")
cmds.connectAttr(node + ".outputTranslateZ", cube + ".translateZ")
cmds.connectAttr(time + ".outTime", node + ".currentTime")
cmds.setAttr(node + ".startTime", cmds.getAttr(time + ".outTime"))

#cmds.file(new=True, force=True)
#cmds.unloadPlugin(os.path.basename(fname))
cmake_minimum_required(VERSION 2.8)
# include the project setting file
include($ENV{DEVKIT_LOCATION}/cmake/pluginEntry.cmake)
# specify project name
set(PROJECT_NAME initialStateNode)
# set SOURCE_FILES
set(SOURCE_FILES
initialStateNode.cpp
)
# set linking libraries
set(LIBRARIES
OpenMaya
Foundation
)
# Build plugin
build_plugin()
#include <string.h>
#include <maya/MPxNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnPlugin.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MVector.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MMatrix.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MStreamUtils.h>
#define Print MStreamUtils::stdErrorStream
class initialStateNode : public MPxNode {
public:
initialStateNode();
~initialStateNode() override;
MStatus compute(const MPlug& plug, MDataBlock& data) override;
MStatus computeOutputTranslate(const MPlug& plug, MDataBlock& data);
static void* creator() { return new initialStateNode(); }
static MStatus initialize();
bool isPassiveOutput(const MPlug& plug) const;
public:
static MTypeId id;
// Attributes
static MObject aInWorldMatrix;
static MObject aStartTime;
static MObject aCurrentTime;
static MObject aOutputTranslate;
static MObject aOutputTranslateX;
static MObject aOutputTranslateY;
static MObject aOutputTranslateZ;
private:
MMatrix _restMatrix;
};
MTypeId initialStateNode::id(0x80012);
MObject initialStateNode::aInWorldMatrix;
MObject initialStateNode::aOutputTranslate;
MObject initialStateNode::aOutputTranslateX;
MObject initialStateNode::aOutputTranslateY;
MObject initialStateNode::aOutputTranslateZ;
MObject initialStateNode::aStartTime;
MObject initialStateNode::aCurrentTime;
initialStateNode::initialStateNode() {}
initialStateNode::~initialStateNode() {}
bool initialStateNode::isPassiveOutput(const MPlug& plug) const {
if (plug == aOutputTranslate ||
plug == aOutputTranslateX ||
plug == aOutputTranslateY ||
plug == aOutputTranslateZ) {
return true;
}
return false;
}
MStatus initialStateNode::initialize() {
MStatus status;
MFnNumericAttribute numFn;
MFnMatrixAttribute matFn;
MFnUnitAttribute uniFn;
aStartTime = uniFn.create("startTime", "stti", MFnUnitAttribute::kTime, 0.0, &status);
aCurrentTime = uniFn.create("currentTime", "cuti", MFnUnitAttribute::kTime, 0.0, &status);
aInWorldMatrix = matFn.create("inWorldMatrix", "inwm");
aOutputTranslateX = numFn.create("outputTranslateX", "outx", MFnNumericData::kDouble, 0.0, &status);
aOutputTranslateY = numFn.create("outputTranslateY", "outy", MFnNumericData::kDouble, 0.0, &status);
aOutputTranslateZ = numFn.create("outputTranslateZ", "outz", MFnNumericData::kDouble, 0.0, &status);
aOutputTranslate = numFn.create("outputTranslate", "outr", aOutputTranslateX, aOutputTranslateY, aOutputTranslateZ);
numFn.setWritable(false);
numFn.setStorable(false);
addAttribute(aStartTime);
addAttribute(aCurrentTime);
addAttribute(aInWorldMatrix);
addAttribute(aOutputTranslate);
attributeAffects(aCurrentTime, aOutputTranslate);
return MS::kSuccess;
}
MStatus initialStateNode::computeOutputTranslate(const MPlug& plug, MDataBlock& datablock) {
MStatus status { MS::kUnknownParameter };
MTransformationMatrix tm { _restMatrix };
MVector translate = tm.translation(MSpace::kTransform);
MDataHandle tx = datablock.outputValue(aOutputTranslateX),
ty = datablock.outputValue(aOutputTranslateY),
tz = datablock.outputValue(aOutputTranslateZ);
tx.set(translate.x);
ty.set(translate.y);
tz.set(translate.z);
datablock.setClean(plug);
return status;
}
MStatus initialStateNode::compute(const MPlug& plug, MDataBlock& datablock) {
MStatus status { MS::kUnknownParameter };
const bool isStartTime = datablock.inputValue(aStartTime).asTime() ==
datablock.inputValue(aCurrentTime).asTime();
if (plug == aOutputTranslate ||
plug == aOutputTranslateX ||
plug == aOutputTranslateY ||
plug == aOutputTranslateZ) {
if (isStartTime) {
Print() << "Updating Rest Matrix, but aren't outputting anything.\n";
_restMatrix = datablock.inputValue(aInWorldMatrix).asMatrix();
}
else {
Print() << "Outputting new translate values\n";
status = computeOutputTranslate(plug, datablock);
}
}
return status;
}
MStatus initializePlugin(MObject obj) {
MStatus status;
MFnPlugin plugin(obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerNode("initialStateNode",
initialStateNode::id,
initialStateNode::creator,
initialStateNode::initialize);
if (!status) {
status.perror("registerNode");
return status;
}
Print() << "==============\n";
Print() << "Initialising\n";
return status;
}
MStatus uninitializePlugin(MObject obj) {
MStatus status;
MFnPlugin plugin(obj);
status = plugin.deregisterNode(initialStateNode::id);
if (!status) {
status.perror("deregisterNode");
return status;
}
Print() << "Deinitialising.\n";
Print() << "==============\n";
return status;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment