Skip to content

Instantly share code, notes, and snippets.

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

⬅️ Back

What about MFnTransform? Does it grant me access to the translate values that are currently being evaluated?

It does, but it also triggers evaluation and thus cycle warnings. But only when stepping a frame. Because of course.

playstep

Questions

Reasons to lie awake at night.

  • Why does DG trigger a cycle warning only on (1) cube selected and (2) stepping frame? Playback is fine, correct behavior without warnings. Also framestepping without being selected is correct.
  • Why does Parallel pull on pCube1.translateX during manipulation, but not DG?

Build & Run

From Powershell.

cd c:\
git clone https://gist.github.com/5b7613404d0f4b383d1b1d18d01254e4.git initialStateNode14
mkdir initialStateNode14/build
cd initialStateNode14/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:\initialState14\build\initialStateNode.mll"
cmds.loadPlugin(fname)

time = "time1"
cube, _ = cmds.polyCube()
cmds.move(0, 2, 0)
node = cmds.createNode("initialStateNode", parent=cube)
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($ENV{DEVKIT_LOCATION}/cmake/pluginEntry.cmake)
set(PROJECT_NAME initialStateNode)
set(SOURCE_FILES
initialStateNode.cpp
)
set(LIBRARIES
OpenMaya
OpenMayaUI
Foundation
)
build_plugin()
#include <string.h>
#include <maya/MPxLocatorNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnPlugin.h>
#include <maya/MTypeId.h>
#include <maya/MString.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>
#include <maya/MFnDagNode.h>
#include <maya/MFnTransform.h>
#include <maya/MQuaternion.h>
#include <maya/MGlobal.h>
#define Print MStreamUtils::stdErrorStream
class initialStateNode : public MPxLocatorNode {
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();
void reset();
bool isPassiveOutput(const MPlug& plug) const override;
public:
static MTypeId id;
static const MString drawDbClassification;
// Attributes
static MObject aStartTime;
static MObject aCurrentTime;
static MObject aGravity;
static MObject aGravityX;
static MObject aGravityY;
static MObject aGravityZ;
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 };
bool _initialised { false };
};
MTypeId initialStateNode::id(0x80012);
const MString initialStateNode::drawDbClassification("drawdb/geometry/initialStateNode");
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);
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(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 };
// In DG mode, evaluation never happens on frames previously
// rendered until this is called. Madness. Why oh why?
Print() << "ogs -reset\n";
MGlobal::executeCommand("ogs -reset");
_outputMatrix = _restMatrix;
_isFirstTime = true;
writeOutputTranslate(datablock);
datablock.setClean(plug);
return status;
}
void initialStateNode::reset() {
MFnDagNode fnDagNode { thisMObject() };
MFnTransform fnTransform { fnDagNode.parent(0) };
// Cycle.. only in in DG, only when framestepping.
MVector translate = fnTransform.getTranslation(MSpace::kTransform);
MTransformationMatrix tm;
tm.setTranslation(translate, MSpace::kTransform);
_restMatrix = tm.asMatrix();
_initialised = true;
}
/**
* Following the start time
*
*/
MStatus initialStateNode::computeFirstState(const MPlug& plug, MDataBlock& datablock) {
Print() << ".......FirstState()\n";
MStatus status { MS::kUnknownParameter };
reset();
_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 };
if (!_initialised) {
reset();
}
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,
MPxNode::kLocatorNode,
&initialStateNode::drawDbClassification);
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