|
#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/MFnMatrixData.h> |
|
#include <maya/MDGContextGuard.h> |
|
#include <maya/MDGContext.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"); |
|
matFn.setAffectsWorldSpace(true); |
|
|
|
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 }; |
|
|
|
// Fetch world matrix from start frame |
|
MTime startTime = datablock.inputValue(aStartTime).asTime(); |
|
MPlug worldMatrixPlug { thisMObject(), aInWorldMatrix }; |
|
MObject obj; |
|
worldMatrixPlug.getValue(obj, MDGContext(startTime)); |
|
MMatrix worldMatrix = MFnMatrixData(obj).matrix(); |
|
|
|
// Visualise output |
|
MTransformationMatrix tm { worldMatrix }; |
|
Print() << " aInWorldMatrix @ frame " << startTime |
|
<< " = " << tm.translation(MSpace::kTransform) |
|
<< "\n"; |
|
|
|
_restMatrix = worldMatrix; |
|
_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) { |
|
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; |
|
} |