I am using a custom setting node (based on MPxNode) to store and pass values to a render override.
These values are queried, calculated and 'sent' to a render override class within the compute() method.
However, for this to happen, a dummy output attribute within the setting node needs to be connected with something visible in the scene to trigger the dirty propagation. Only then will the compute node run and pass the updated values to the render override.
Ideally, I would like to just query and pass the input attributes to the render override whenever these are changed, without having to resort to workarounds i.e., affecting a dummy output attribute just to trigger the dirty propagation and get the job done.
Is there any way to do this? Any help is appreciated.
Solved! Go to Solution.
I am using a custom setting node (based on MPxNode) to store and pass values to a render override.
These values are queried, calculated and 'sent' to a render override class within the compute() method.
However, for this to happen, a dummy output attribute within the setting node needs to be connected with something visible in the scene to trigger the dirty propagation. Only then will the compute node run and pass the updated values to the render override.
Ideally, I would like to just query and pass the input attributes to the render override whenever these are changed, without having to resort to workarounds i.e., affecting a dummy output attribute just to trigger the dirty propagation and get the job done.
Is there any way to do this? Any help is appreciated.
Solved! Go to Solution.
Solved by drsanty. Go to Solution.
I don't think there is, since something needs to depend on the plug in the first place for it to be evaluated. I have my render override search for the settings singleton in MRenderOverride::setup and pull data in.
Optimizing it so it only refreshes settings when something changes is a headache, though. The settings node updates a sequence number that the override can compare, which happens in MPxNode::compute, connectionMade, connectionBroken and setInternalValue. All attributes are marked internal, and affect an "evaluate" attribute. At the start of setup() evaluate is evaluated to trigger any pending compute calls, then the sequence number is checked.
I'm not completely sure it still won't have issues with evaluation cache, since I haven't used that much...
I don't think there is, since something needs to depend on the plug in the first place for it to be evaluated. I have my render override search for the settings singleton in MRenderOverride::setup and pull data in.
Optimizing it so it only refreshes settings when something changes is a headache, though. The settings node updates a sequence number that the override can compare, which happens in MPxNode::compute, connectionMade, connectionBroken and setInternalValue. All attributes are marked internal, and affect an "evaluate" attribute. At the start of setup() evaluate is evaluated to trigger any pending compute calls, then the sequence number is checked.
I'm not completely sure it still won't have issues with evaluation cache, since I haven't used that much...
Hi zewt, thanks for your input!
I managed to create a cleaner solution with a hint you gave me, instead of connecting the setting node to something visible, I can just evaluate it at the beginning of the setup() method within the render override.
// check if setting node needs to be evaluated MString cmd; cmd.format("dgeval ^1s.evaluate", SETTINGNODE); MGlobal::executeCommand(cmd);
If the setting node is dirty, the evaluation will trigger the compute() method and pass all the changed settings to the render override. This will only happen when attributes have changed from one specific node in the scene (SETTINGNODE)---which also solves issues we were having with referenced setting nodes.
Hi zewt, thanks for your input!
I managed to create a cleaner solution with a hint you gave me, instead of connecting the setting node to something visible, I can just evaluate it at the beginning of the setup() method within the render override.
// check if setting node needs to be evaluated MString cmd; cmd.format("dgeval ^1s.evaluate", SETTINGNODE); MGlobal::executeCommand(cmd);
If the setting node is dirty, the evaluation will trigger the compute() method and pass all the changed settings to the render override. This will only happen when attributes have changed from one specific node in the scene (SETTINGNODE)---which also solves issues we were having with referenced setting nodes.
The way I force evaluation for this is with MPlug::asMDataHandle, which might be a little nicer than making a MEL call: MPlug(node, evaluateAttr).asMDataHandle();
Be careful if you're assuming that referenced settings nodes won't be computed, since that might be hard to guarantee. For example, simply selecting the node and causing it to be displayed in the AE or node editor might trigger a compute.
The way I force evaluation for this is with MPlug::asMDataHandle, which might be a little nicer than making a MEL call: MPlug(node, evaluateAttr).asMDataHandle();
Be careful if you're assuming that referenced settings nodes won't be computed, since that might be hard to guarantee. For example, simply selecting the node and causing it to be displayed in the AE or node editor might trigger a compute.
That's interesting, I didn't know that you could force the evaluation by just getting the MDataHandle from a plug, thanks for your help!
Showing the node in the Attribute Editor does indeed trigger an evaluation. We will make sure artists can only select the right settings node in case of loading references.
I also noticed something odd. When you set a numeric attribute as a color with setUsedAsColor(), modifying the value in the Attribute Editor doesn't refresh the Viewport. That means that setup() is never run->the evaluation doesn't happen->the color within the render override doesn't change.
In case anyone else encounters this issue, I managed to solve this by forcing a refresh when these types of attributes are changed by overriding the setDependetsDirty() method within MPxNode.
MStatus SettingsNode::setDependentsDirty(const MPlug& inPlug, MPlugArray& affectedPlugs) { // force viewport refresh when changing attributes that are set as color MAnimControl animControl; if (!animControl.isPlaying()) { MFnAttribute fnAttr(inPlug.attribute()); if (fnAttr.isUsedAsColor()) { MGlobal::executeCommandOnIdle("refresh;"); } } return MS::kSuccess; }
That's interesting, I didn't know that you could force the evaluation by just getting the MDataHandle from a plug, thanks for your help!
Showing the node in the Attribute Editor does indeed trigger an evaluation. We will make sure artists can only select the right settings node in case of loading references.
I also noticed something odd. When you set a numeric attribute as a color with setUsedAsColor(), modifying the value in the Attribute Editor doesn't refresh the Viewport. That means that setup() is never run->the evaluation doesn't happen->the color within the render override doesn't change.
In case anyone else encounters this issue, I managed to solve this by forcing a refresh when these types of attributes are changed by overriding the setDependetsDirty() method within MPxNode.
MStatus SettingsNode::setDependentsDirty(const MPlug& inPlug, MPlugArray& affectedPlugs) { // force viewport refresh when changing attributes that are set as color MAnimControl animControl; if (!animControl.isPlaying()) { MFnAttribute fnAttr(inPlug.attribute()); if (fnAttr.isUsedAsColor()) { MGlobal::executeCommandOnIdle("refresh;"); } } return MS::kSuccess; }
Be careful, setDependentsDirty isn't called during parallel evaluation (see https://damassets.autodesk.net/content/dam/autodesk/www/Company/files/Parallel_Maya.pdf). FYI, there's M3dView::scheduleRefreshAllViews if you really need to force a refresh.
It does refresh for me, though. Actually it refreshes if I change anything, even attributes that have nothing to do with the viewport, but I guess that makes more sense than trying to eliminate a few viewport refreshes. It seems like setAffectsAppearance on attributes does nothing and it just assumes any change needs a refresh. I'm not sure what might be different, I don't think I'm doing anything special there...
Be careful, setDependentsDirty isn't called during parallel evaluation (see https://damassets.autodesk.net/content/dam/autodesk/www/Company/files/Parallel_Maya.pdf). FYI, there's M3dView::scheduleRefreshAllViews if you really need to force a refresh.
It does refresh for me, though. Actually it refreshes if I change anything, even attributes that have nothing to do with the viewport, but I guess that makes more sense than trying to eliminate a few viewport refreshes. It seems like setAffectsAppearance on attributes does nothing and it just assumes any change needs a refresh. I'm not sure what might be different, I don't think I'm doing anything special there...
Hey @zewt and anyone else interested,
since Autodesk kindly reminded me to "resolve" this thread, this is what we are ultimately using:
MStatus ConfigNode::setDependentsDirty(const MPlug& inPlug, MPlugArray& affectedPlugs) {
// force viewport refresh with unconnected node
MAnimControl animControl;
if (!animControl.isPlaying()) {
M3dView::scheduleRefreshAllViews();
}
return MS::kSuccess;
}
For our use purposes (just scheduling a viewport refresh) and based on our testing (Maya 2018+), `setDependetsDirty()` is being called also during parallel evaluation, so we haven't encountered any issues yet.
Hey @zewt and anyone else interested,
since Autodesk kindly reminded me to "resolve" this thread, this is what we are ultimately using:
MStatus ConfigNode::setDependentsDirty(const MPlug& inPlug, MPlugArray& affectedPlugs) {
// force viewport refresh with unconnected node
MAnimControl animControl;
if (!animControl.isPlaying()) {
M3dView::scheduleRefreshAllViews();
}
return MS::kSuccess;
}
For our use purposes (just scheduling a viewport refresh) and based on our testing (Maya 2018+), `setDependetsDirty()` is being called also during parallel evaluation, so we haven't encountered any issues yet.
Can't find what you're looking for? Ask the community or share your knowledge.