Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active February 10, 2021 15:33
Show Gist options
  • Save mottosso/26e4bace91cc54580f3875226498a530 to your computer and use it in GitHub Desktop.
Save mottosso/26e4bace91cc54580f3875226498a530 to your computer and use it in GitHub Desktop.
Editable Initial State - Attempt 3

⬅️ Back

So outputValue returns a value from the previous evaluation.. Ok. What if we rely on that?

initialstate45

And yes! That works! Almost..!

It's not clear how inWorldMatrix is ever evaluated here. We aren't actually pulling it at all. We're only ever calling outputValue. So, in theory, it should never be updated..? It must be updated externally, by Maya. It's possible that the value we are getting is the latest updated value from the source connection? In this case pCube1. That would explain why it gets updated, as Maya uses it to render the cube. But it would also throw into question who owns this MDataBlock if not our own node. Is it shared amongst nodes? :O I think not.

In either case, it all falls apart when we change the initial state with anything other than the manipulator..

initialstate46

Here, outputTranslate feeds into pairBlend which feeds into pCube1.translate. And when pairBlend produces new output, it doesn't pull on outputTranslate anymore, thus not causing anything to evaluate in our node. :( So it would seem that the manipulator is the one responsible for pulling on the attributes.


Build & Run

From Powershell.

cd c:\
git clone https://gist.github.com/26e4bace91cc54580f3875226498a530.git initialStateNode3
mkdir initialStateNode3/build
cd initialStateNode3/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:\initialState3\build\initialStateNode.mll"
cmds.loadPlugin(fname)

time = "time1"
cube, _ = cmds.polyCube()
cmds.move(-3, 15, -7)
cmds.scale(4, 4, 4)
node = cmds.createNode("initialStateNode")
cmds.setAttr(node + ".startTime", cmds.getAttr(time + ".outTime"))
cmds.setAttr(node + ".gravity", 0, -9.81, 0)
cmds.connectAttr(time + ".outTime", node + ".currentTime")
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.file(new=True, force=True)
#cmds.unloadPlugin(os.path.basename(fname))
cmake_minimum_required(VERSION 2.8)
include($ENV{DEVKIT_LOCATION}/cmake/pluginEntry.cmake)
set(PROJECT_NAME initialStateNode)
set(SOURCE_FILES
initialStateNode.cpp
)
set(LIBRARIES
OpenMaya
Foundation
)
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 computeFirstState(const MPlug& plug, MDataBlock& data);
MStatus computeCurrentState(const MPlug& plug, MDataBlock& data);
MStatus writeOutputTranslate(MDataBlock& datablock);
MStatus stepSimulation(MDataBlock& datablock);
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 aGravity;
static MObject aGravityX;
static MObject aGravityY;
static MObject aGravityZ;
static MObject aInWorldMatrix;
static MObject aOutputTranslate;
static MObject aOutputTranslateX;
static MObject aOutputTranslateY;
static MObject aOutputTranslateZ;
private:
bool _isStartTime { false };
bool _isFirstTime { false };
MTime _lastTime { };
MTime _currentTime { };
MMatrix _restMatrix {};
MMatrix _outputMatrix {};
MVector _velocity { 0, 0, 0 };
};
MTypeId initialStateNode::id(0x80012);
MObject initialStateNode::aInWorldMatrix;
MObject initialStateNode::aOutputTranslate;
MObject initialStateNode::aOutputTranslateX;
MObject initialStateNode::aOutputTranslateY;
MObject initialStateNode::aOutputTranslateZ;
MObject initialStateNode::aGravity;
MObject initialStateNode::aGravityX;
MObject initialStateNode::aGravityY;
MObject initialStateNode::aGravityZ;
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 = uniFn.create("outputTranslateX", "outx", MFnUnitAttribute::kDistance, 0.0, &status);
aOutputTranslateY = uniFn.create("outputTranslateY", "outy", MFnUnitAttribute::kDistance, 0.0, &status);
aOutputTranslateZ = uniFn.create("outputTranslateZ", "outz", MFnUnitAttribute::kDistance, 0.0, &status);
aOutputTranslate = numFn.create("outputTranslate", "outr", aOutputTranslateX, aOutputTranslateY, aOutputTranslateZ);
uniFn.setWritable(false);
uniFn.setStorable(false);
aGravityX = uniFn.create("gravityX", "grvx", MFnUnitAttribute::kDistance, 0.0, &status);
aGravityY = uniFn.create("gravityY", "grvy", MFnUnitAttribute::kDistance, -981.0, &status);
aGravityZ = uniFn.create("gravityZ", "grvz", MFnUnitAttribute::kDistance, 0.0, &status);
aGravity = numFn.create("gravity", "grav", aGravityX, aGravityY, aGravityZ);
addAttribute(aStartTime);
addAttribute(aCurrentTime);
addAttribute(aInWorldMatrix);
addAttribute(aOutputTranslate);
addAttribute(aGravity);
attributeAffects(aGravity, 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 };
_outputMatrix = _restMatrix;
_isFirstTime = true;
writeOutputTranslate(datablock);
datablock.setClean(plug);
return status;
}
/**
* Following the start time
*
*/
MStatus initialStateNode::computeFirstState(const MPlug& plug, MDataBlock& datablock) {
Print() << ".......FirstState()\n";
MStatus status { MS::kUnknownParameter };
_restMatrix = datablock.outputValue(aInWorldMatrix).asMatrix();
_outputMatrix = _restMatrix;
_velocity = { 0, 0, 0 };
_isFirstTime = false;
stepSimulation(datablock);
writeOutputTranslate(datablock);
datablock.setClean(plug);
return status;
}
/**
* Following the first frame
*
*/
MStatus initialStateNode::computeCurrentState(const MPlug& plug, MDataBlock& datablock) {
Print() << ".......CurrentState()\n";
MStatus status { MS::kSuccess };
stepSimulation(datablock);
writeOutputTranslate(datablock);
datablock.setClean(plug);
return status;
}
/**
* Apply gravity and update position
*
*/
MStatus initialStateNode::stepSimulation(MDataBlock& datablock) {
MStatus status { MS::kSuccess };
MTransformationMatrix tm { _outputMatrix };
double deltaTime = (_currentTime - _lastTime).as(MTime::kSeconds);
if (deltaTime > 0.0) {
Print() << "....... Stepping simulation..\n";
MVector gravity = datablock.inputValue(aGravity, &status).asDouble3();
_velocity += gravity * deltaTime;
tm.addTranslation(_velocity, MSpace::kTransform);
}
_outputMatrix = tm.asMatrix();
return status;
}
MStatus initialStateNode::writeOutputTranslate(MDataBlock& datablock) {
MStatus status { MS::kSuccess };
MTransformationMatrix tm { _outputMatrix };
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);
return status;
}
MStatus initialStateNode::compute(const MPlug& plug, MDataBlock& datablock) {
MStatus status { MS::kUnknownParameter };
const MTime currentTime = datablock.inputValue(aCurrentTime).asTime();
const MTime startTime = datablock.inputValue(aStartTime).asTime();
_currentTime = currentTime;
_isStartTime = currentTime <= startTime;
if (plug == aOutputTranslate ||
plug == aOutputTranslateX ||
plug == aOutputTranslateY ||
plug == aOutputTranslateZ) {
if (_isStartTime) status = computeStartState(plug, datablock);
else if (_isFirstTime) status = computeFirstState(plug, datablock);
else status = computeCurrentState(plug, datablock);
}
_lastTime = currentTime;
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