Skip to content

Instantly share code, notes, and snippets.

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

⬅️ Back

What if we call datablock.outputValue instead of .inputValue, thus avoiding the need to actually evaluate inWorldMatrix?


Updates

To clarify what's going on, let's expand on the plug-in.

  1. Add gravity
  2. Refactor compute to computeStartState and computeCurrentState

Such that "start state" happens on the start frame, and "current state" on subsequent frames. On the start frame, we'll initialise the position to whatever inWorldMatrix is, and then move the connected cube downwards with gravity on subsequent frames.

attempt2_1

We no longer get cycle warnings, because this isn't a cycle. We've broken that loop; whenever inWorldMatrix is queried, it's fetching the last evaluated value rather than evaluating anew.

But we've now got two problems.

  1. We lose track of the start position
  2. The start position doesn't match exactly with where we left it

(1) Notice how when arriving on the start frame, it just stays put rather than returning to the initial position. (2) Is a problem with calling outputValue instead of inputValue. We aren't getting the latest up-to-date value.

Here's a close-up.

attempt2_2

Notice how I move it to one location, but once it starts "falling" it moves slightly to the right? That's because it was the last value of pCube1.worldMatrix[0] to be evaluated before being picked up by our node. It's the position from the last update. The previously rendered position.

And that's bad.


Build & Run

From Powershell.

cd c:\
git clone https://gist.github.com/1eacf4f68c53e78a41e0e903ace3da66.git initialStateNode
mkdir initialStateNode2/build
cd initialStateNode2/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:\initialState2\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 computeStartState(const MPlug& plug, MDataBlock& data);
MStatus computeCurrentState(const MPlug& plug, MDataBlock& data);
static void* creator() { return new initialStateNode(); }
static MStatus initialize();
bool isPassiveOutput(const MPlug& plug) const override;
public:
static MTypeId id;
// Attributes
static MObject aStartTime;
static MObject aCurrentTime;
static MObject aInWorldMatrix;
static MObject aOutputTranslate;
static MObject aOutputTranslateX;
static MObject aOutputTranslateY;
static MObject aOutputTranslateZ;
private:
MTime _lastTime {};
MMatrix _restMatrix {};
MMatrix _outputMatrix {};
MVector _velocity { 0, 0, 0 };
MVector _gravity { 0, -981.0, 0 };
};
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;
}
/**
* Read and store rest matrix
*
*/
MStatus initialStateNode::computeStartState(const MPlug& plug, MDataBlock& datablock) {
Print() << "computeStartState()\n";
MStatus status { MS::kSuccess };
_restMatrix = datablock.outputValue(aInWorldMatrix).asMatrix();
_outputMatrix = _restMatrix;
_velocity = { 0, 0, 0 };
return status;
}
/**
* Apply gravity and update position
*
*/
MStatus initialStateNode::computeCurrentState(const MPlug& plug, MDataBlock& datablock) {
Print() << "computeCurrentState()\n";
MStatus status { MS::kUnknownParameter };
MTransformationMatrix tm { _outputMatrix };
// Step "simulation"
_velocity += _gravity * 0.001;
tm.addTranslation(_velocity, MSpace::kTransform);
// Output
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) status = computeStartState(plug, datablock);
else status = computeCurrentState(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