|
#include <string.h> |
|
|
|
#include <maya/MPxLocatorNode.h> |
|
|
|
#include <maya/MFnDependencyNode.h> |
|
#include <maya/MFnNumericAttribute.h> |
|
#include <maya/MFnMatrixAttribute.h> |
|
#include <maya/MFnUnitAttribute.h> |
|
#include <maya/MFnPlugin.h> |
|
|
|
#include <maya/MPxDrawOverride.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/MFnMatrixData.h> |
|
#include <maya/MStreamUtils.h> |
|
#include <maya/MDrawRegistry.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); |
|
void saveRestMatrix(MMatrix mat); |
|
|
|
static void* creator() { return new initialStateNode(); } |
|
static MStatus initialize(); |
|
|
|
bool isPassiveOutput(const MPlug& plug) const override; |
|
|
|
public: |
|
static MTypeId id; |
|
static const MString drawDbClassification; |
|
static const MString drawRegistrantId; |
|
|
|
// 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 { }; |
|
double _deltaSeconds { }; |
|
MTime _currentTime { }; |
|
MMatrix _restMatrix {}; |
|
MMatrix _outputMatrix {}; |
|
MMatrix _drawWorldMatrix {}; |
|
MVector _velocity { 0, 0, 0 }; |
|
}; |
|
|
|
MTypeId initialStateNode::id(0x80012); |
|
const MString initialStateNode::drawDbClassification("drawdb/geometry/initialStateNode"); |
|
const MString initialStateNode::drawRegistrantId("initialStateNodePlugin"); |
|
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; |
|
|
|
static bool gTimeDirty { true }; |
|
|
|
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 }; |
|
|
|
// Distinguish between user input and changes to time |
|
if (_deltaSeconds) { |
|
Print() << " --> Restoring\n"; |
|
} |
|
else { |
|
Print() << " <-- Recording\n"; |
|
_restMatrix = _drawWorldMatrix; |
|
} |
|
|
|
_outputMatrix = _restMatrix; |
|
_isFirstTime = true; |
|
|
|
writeOutputTranslate(datablock); |
|
|
|
datablock.setClean(plug); |
|
|
|
return status; |
|
} |
|
|
|
|
|
/** |
|
* Following the start time |
|
* |
|
*/ |
|
MStatus initialStateNode::computeFirstState(const MPlug& plug, MDataBlock& datablock) { |
|
Print() << "computeFirstState()\n"; |
|
MStatus status { MS::kUnknownParameter }; |
|
|
|
_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() << "computeCurrentState()\n"; |
|
|
|
MStatus status { MS::kSuccess }; |
|
|
|
stepSimulation(datablock); |
|
|
|
writeOutputTranslate(datablock); |
|
datablock.setClean(plug); |
|
|
|
return status; |
|
} |
|
|
|
|
|
void initialStateNode::saveRestMatrix(MMatrix mat) { |
|
_drawWorldMatrix = mat; |
|
} |
|
|
|
|
|
/** |
|
* Apply gravity and update position |
|
* |
|
*/ |
|
MStatus initialStateNode::stepSimulation(MDataBlock& datablock) { |
|
MStatus status { MS::kSuccess }; |
|
MTransformationMatrix tm { _outputMatrix }; |
|
|
|
if (_deltaSeconds > 0.0) { |
|
MVector gravity = datablock.inputValue(aGravity, &status).asDouble3(); |
|
_velocity += gravity * _deltaSeconds; |
|
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; |
|
_deltaSeconds = (_currentTime - _lastTime).as(MTime::kSeconds); |
|
|
|
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; |
|
_deltaSeconds = 0.0; |
|
|
|
return status; |
|
} |
|
|
|
|
|
/** |
|
* Read from whatever matrix rendering uses, and pass it back into our node. |
|
* |
|
* |
|
* |
|
*/ |
|
class initialStateNodeDrawOverride : public MHWRender::MPxDrawOverride { |
|
public: |
|
static MHWRender::MPxDrawOverride* Creator(const MObject& obj) { return new initialStateNodeDrawOverride(obj); } |
|
~initialStateNodeDrawOverride() override { _node = nullptr; } |
|
|
|
MUserData* prepareForDraw(const MDagPath&, |
|
const MDagPath&, |
|
const MHWRender::MFrameContext&, |
|
MUserData*) override { return nullptr; } |
|
|
|
void addUIDrawables(const MDagPath&, |
|
MHWRender::MUIDrawManager&, |
|
const MHWRender::MFrameContext&, |
|
const MUserData*) override; |
|
|
|
// Constant values |
|
MHWRender::DrawAPI supportedDrawAPIs() const override { return MHWRender::kDirectX11 | MHWRender::kOpenGLCoreProfile; } |
|
bool isBounded(const MDagPath&, const MDagPath&) const override { return false; } |
|
bool hasUIDrawables() const override { return true; } |
|
|
|
private: |
|
initialStateNodeDrawOverride(const MObject&); |
|
initialStateNode* _node; |
|
}; |
|
|
|
|
|
initialStateNodeDrawOverride::initialStateNodeDrawOverride(const MObject& obj) |
|
: MHWRender::MPxDrawOverride(obj, nullptr, true), _node(nullptr) { |
|
|
|
MStatus status; |
|
MFnDependencyNode fn(obj, &status); |
|
|
|
// Maintain handle to the parent fn |
|
if (status) { |
|
_node = dynamic_cast<initialStateNode*>(fn.userNode()); |
|
} |
|
} |
|
|
|
|
|
void initialStateNodeDrawOverride::addUIDrawables(const MDagPath& objPath, |
|
MHWRender::MUIDrawManager&, |
|
const MHWRender::MFrameContext&, |
|
const MUserData*) { |
|
MObject obj = objPath.node(); |
|
MPlug inWorldMatrixPlug { obj, initialStateNode::aInWorldMatrix }; |
|
MObject inWorldMatrixObj = inWorldMatrixPlug.asMObject(); |
|
MMatrix inWorldMatrix = MFnMatrixData(inWorldMatrixObj).matrix(); |
|
|
|
_node->saveRestMatrix(inWorldMatrix); |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
MHWRender::MDrawRegistry::registerDrawOverrideCreator( |
|
initialStateNode::drawDbClassification, |
|
initialStateNode::drawRegistrantId, |
|
initialStateNodeDrawOverride::Creator |
|
); |
|
|
|
Print() << "==============\n"; |
|
Print() << "Initialising\n"; |
|
|
|
return status; |
|
} |
|
|
|
|
|
MStatus uninitializePlugin(MObject obj) { |
|
MStatus status; |
|
MFnPlugin plugin(obj); |
|
|
|
MHWRender::MDrawRegistry::deregisterDrawOverrideCreator( |
|
initialStateNode::drawDbClassification, |
|
initialStateNode::drawRegistrantId |
|
); |
|
|
|
status = plugin.deregisterNode(initialStateNode::id); |
|
if (!status) { |
|
status.perror("deregisterNode"); |
|
return status; |
|
} |
|
|
|
Print() << "Deinitialising\n"; |
|
Print() << "==============\n"; |
|
|
|
return status; |
|
} |