From 12e9546f9f1e6f5a36820de80976d5bbb5231a7f Mon Sep 17 00:00:00 2001 From: David Lanier Date: Mon, 4 Mar 2024 17:59:03 +0100 Subject: [PATCH] HYDRA-746 : Apply Pierre's changes to this example --- .../mhFlowViewportAPILocator.cpp | 175 +++++++++--------- .../render/mayaToHydra/testFlowViewportAPI.py | 75 ++------ 2 files changed, 101 insertions(+), 149 deletions(-) diff --git a/lib/mayaHydra/flowViewportAPIExamples/flowViewportAPILocator/mhFlowViewportAPILocator.cpp b/lib/mayaHydra/flowViewportAPIExamples/flowViewportAPILocator/mhFlowViewportAPILocator.cpp index 35adc947cb..a0bacb4833 100644 --- a/lib/mayaHydra/flowViewportAPIExamples/flowViewportAPILocator/mhFlowViewportAPILocator.cpp +++ b/lib/mayaHydra/flowViewportAPIExamples/flowViewportAPILocator/mhFlowViewportAPILocator.cpp @@ -40,6 +40,8 @@ #include #include #include +#include +#include //We use a locator node to deal with creating and filtering hydra primitives as an example. //But you could use another kind of maya plugin. @@ -48,6 +50,11 @@ createNode("MhFlowViewportAPILocator") */ +namespace { +void nodeAddedToModel(MObject& node, void* clientData); +void nodeRemovedFromModel(MObject& node, void* clientData); +} + PXR_NAMESPACE_USING_DIRECTIVE ///Maya Locator node subclass to create filtering and data producer scene indices example, to be used with the flow viewport API. @@ -67,7 +74,6 @@ class MhFlowViewportAPILocator : public MPxLocatorNode void getCacheSetup(const MEvaluationNode& evalNode, MNodeCacheDisablingInfo& disablingInfo, MNodeCacheSetupInfo& cacheSetupInfo, MObjectArray& monitoredAttributes) const override; void setCubeGridParametersFromAttributes(); - void setupFlowViewportInterfaces(); //static members static void* creator(); @@ -85,14 +91,18 @@ class MhFlowViewportAPILocator : public MPxLocatorNode static MObject mCubeOpacity; static MObject mCubesUseInstancing; static MObject mCubesDeltaTrans; - static MObject mDummyInput;//Dummy input to trigger a call to compute - static MObject mDummyOutput;//Dummy output to trigger a call to compute ///3D Grid of cube mesh primitives creation parameters for the data producer scene index Fvp::DataProducerSceneIndexExample::CubeGridCreationParams _cubeGridParams; ///_hydraViewportDataProducerSceneIndexExample is what will inject the 3D grid of Hydra cube mesh primitives into the viewport Fvp::DataProducerSceneIndexExample _hydraViewportDataProducerSceneIndexExample; + // Callback when the footprint node is added to the model (create / + // undo-delete) + void addedToModelCb(); + // Callback when the footprint node is removed from model (delete) + void removedFromModelCb(); + protected: /// _hydraViewportFilteringSceneIndexClientExample is the filtering scene index example for a Hydra viewport scene index. std::shared_ptr _hydraViewportFilteringSceneIndexClientExample; @@ -101,16 +111,17 @@ class MhFlowViewportAPILocator : public MPxLocatorNode ///To be used in hydra viewport API to pass the Maya node's MObject for setting callbacks for filtering and data producer scene indices MObjectHandle _thisMObject; ///To hold the attributeChangedCallback Id to be able to react when the 3D grid creation parameters attributes from this node change. - MCallbackId _cbAttributeChangedId = 0; + MCallbackId _cbAttributeChangedId {0}; ///To hold the afterOpenCallback Id to be able to react when a File Open has happened. - MCallbackId _cbAfterOpenId = 0; - + MCallbackId _cbAfterOpenId {0}; + /// to hold the nodeAddedToModel callback id + MCallbackId _nodeAddedToModelCbId{0}; + /// to hold the nodeRemovedFromModel callback id + MCallbackId _nodeRemovedFromModelCbId{0}; + private: /// Private Constructor MhFlowViewportAPILocator() {} - - ///Update the MObject of this node - void _UpdateThisMObject(); }; namespace @@ -125,10 +136,6 @@ namespace MhFlowViewportAPILocator* flowViewportAPIMayaLocator = reinterpret_cast(dataProducerSceneIndexData); MPlug parentPlug = plug.parent(); - if (plug == flowViewportAPIMayaLocator->mDummyInput || plug == flowViewportAPIMayaLocator->mDummyOutput){ - return; //These attributes are not related to the cubes grid - } - if (plug == flowViewportAPIMayaLocator->mNumCubeLevelsX){ flowViewportAPIMayaLocator->_cubeGridParams._numLevelsX = plug.asInt(); }else @@ -222,8 +229,29 @@ namespace MhFlowViewportAPILocator* flowViewportAPIMayaLocator = reinterpret_cast(clientData); flowViewportAPIMayaLocator->setCubeGridParametersFromAttributes(); - flowViewportAPIMayaLocator->setupFlowViewportInterfaces(); + flowViewportAPIMayaLocator->addedToModelCb(); } + + void nodeAddedToModel(MObject& node, void* /* clientData */) + { + auto fpNode = reinterpret_cast(MFnDagNode(node).userNode()); + if (!TF_VERIFY(fpNode)) { + return; + } + + fpNode->addedToModelCb(); + } + + void nodeRemovedFromModel(MObject& node, void* /* clientData */) + { + auto fpNode = reinterpret_cast(MFnDagNode(node).userNode()); + if (!TF_VERIFY(fpNode)) { + return; + } + + fpNode->removedFromModelCb(); + } + }//end of anonymous namespace //Initialization of static members @@ -239,8 +267,6 @@ MObject MhFlowViewportAPILocator::mCubeColor; MObject MhFlowViewportAPILocator::mCubeOpacity; MObject MhFlowViewportAPILocator::mCubesUseInstancing; MObject MhFlowViewportAPILocator::mCubesDeltaTrans; -MObject MhFlowViewportAPILocator::mDummyInput; -MObject MhFlowViewportAPILocator::mDummyOutput; void MhFlowViewportAPILocator::postConstructor() { @@ -277,20 +303,20 @@ void MhFlowViewportAPILocator::postConstructor() nullptr);//DCC node will be filled later setCubeGridParametersFromAttributes(); + + MObject obj = thisMObject(); + _nodeAddedToModelCbId = MModelMessage::addNodeAddedToModelCallback(obj, nodeAddedToModel); + _nodeRemovedFromModelCbId = MModelMessage::addNodeRemovedFromModelCallback(obj, nodeRemovedFromModel); } //This is called only when our node is destroyed and the undo queue flushed. MhFlowViewportAPILocator::~MhFlowViewportAPILocator() { //Remove the callbacks - if (_cbAttributeChangedId){ - CHECK_MSTATUS(MMessage::removeCallback(_cbAttributeChangedId)); - _cbAttributeChangedId = 0; - } - - if (_cbAfterOpenId){ - CHECK_MSTATUS(MSceneMessage::removeCallback(_cbAfterOpenId)); - _cbAfterOpenId = 0; + for(auto cbId : {_cbAfterOpenId, _cbAttributeChangedId, _nodeAddedToModelCbId, _nodeRemovedFromModelCbId}) { + if (cbId) { + CHECK_MSTATUS(MMessage::removeCallback(cbId)); + } } //The DataProducerSceneIndexExample in its destructor removes itself by calling DataProducerSceneIndexExample::RemoveDataProducerSceneIndex() @@ -339,58 +365,8 @@ void MhFlowViewportAPILocator::setCubeGridParametersFromAttributes() _hydraViewportDataProducerSceneIndexExample.setCubeGridParams(_cubeGridParams); } -void MhFlowViewportAPILocator::_UpdateThisMObject() -{ - if (_thisMObject.isValid()){ - return; - } - - _thisMObject = thisMObject(); -} - -void MhFlowViewportAPILocator::setupFlowViewportInterfaces() -{ - _UpdateThisMObject(); - - //Set the maya node as a parent for this data producer scene index so that when the node is hidden/deleted/moved it gets applied to the prims produced - MObject obj = _thisMObject.object(); - if (obj.isNull()){ - perror("ERROR : _thisMObject.object() is null, using the maya node to move/hide the prims won't be supported"); - return; - } - - //Remove any existing callback - if (_cbAttributeChangedId){ - CHECK_MSTATUS(MMessage::removeCallback(_cbAttributeChangedId)); - _cbAttributeChangedId = 0; - } - - //Add the callback when an attribute of this node changes - _cbAttributeChangedId = MNodeMessage::addAttributeChangedCallback(obj, attributeChangedCallback, ((void*)this)); - - _hydraViewportDataProducerSceneIndexExample.setContainerNode(&obj); - _hydraViewportDataProducerSceneIndexExample.addDataProducerSceneIndex(); - - //Register a filtering scene index client - Fvp::FilteringSceneIndexInterface& filteringSceneIndexInterface = Fvp::FilteringSceneIndexInterface::get(); - - //Store the MObject* of the maya node in various classes - _hydraViewportFilteringSceneIndexClientExample->setDccNode(&obj); - - //Register this filtering scene index client, so it can append custom filtering scene indices to Hydra viewport scene indices - const bool bResult = filteringSceneIndexInterface.registerFilteringSceneIndexClient(_hydraViewportFilteringSceneIndexClientExample); - if(! bResult){ - perror("ERROR : filteringSceneIndexInterface.registerFilteringSceneIndexClient returned false"); - } -} - MStatus MhFlowViewportAPILocator::compute( const MPlug& plug, MDataBlock& dataBlock) { - //The MObject can change if the node gets deleted and deletion being undone - if (! _thisMObject.isValid()){ - setupFlowViewportInterfaces(); - } - return MS::kSuccess; } @@ -437,6 +413,43 @@ void* MhFlowViewportAPILocator::creator() return new MhFlowViewportAPILocator; } +void MhFlowViewportAPILocator::addedToModelCb() +{ + static const SdfPath noPrefix = SdfPath::AbsoluteRootPath(); + + //Add the callback when an attribute of this node changes + MObject obj = thisMObject(); + _cbAttributeChangedId = MNodeMessage::addAttributeChangedCallback(obj, attributeChangedCallback, ((void*)this)); + + _hydraViewportDataProducerSceneIndexExample.setContainerNode(&obj); + _hydraViewportDataProducerSceneIndexExample.addDataProducerSceneIndex(); + + //Store the MObject* of the maya node in various classes + _hydraViewportFilteringSceneIndexClientExample->setDccNode(&obj); + + //Register this filtering scene index client, so it can append custom filtering scene indices to Hydra viewport scene indices + Fvp::FilteringSceneIndexInterface& filteringSceneIndexInterface = Fvp::FilteringSceneIndexInterface::get(); + const bool bResult = filteringSceneIndexInterface.registerFilteringSceneIndexClient(_hydraViewportFilteringSceneIndexClientExample); + if(! bResult){ + perror("ERROR : filteringSceneIndexInterface.registerFilteringSceneIndexClient returned false"); + } +} + +void MhFlowViewportAPILocator::removedFromModelCb() +{ + //Remove the callback + if (_cbAttributeChangedId){ + CHECK_MSTATUS(MMessage::removeCallback(_cbAttributeChangedId)); + _cbAttributeChangedId = 0; + } + + //Remove the data producer scene index. + _hydraViewportDataProducerSceneIndexExample.removeDataProducerSceneIndex(); + + Fvp::FilteringSceneIndexInterface& filteringSceneIndexInterface = Fvp::FilteringSceneIndexInterface::get(); + filteringSceneIndexInterface.unregisterFilteringSceneIndexClient(_hydraViewportFilteringSceneIndexClientExample); +} + //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Plugin Registration @@ -500,16 +513,6 @@ MStatus MhFlowViewportAPILocator::initialize() MAKE_INPUT(nAttr); CHECK_MSTATUS ( nAttr.setDefault(5.0, 5.0, 5.0) ); - //Create dummy output attribute to trigger a call to the compute function on demand. as it's in the compute fonction that we add our scene indices - mDummyInput = nAttr.create("dummyInput", "dI", MFnNumericData::kInt, 1.0, &status); - MAKE_INPUT(nAttr); - CHECK_MSTATUS ( nAttr.setDefault(1) ); - - //Create dummy output attribute to trigger a call to the compute function on demand. as it's in the compute fonction that we add our scene indices - mDummyOutput = nAttr.create("dummyOutput", "dO", MFnNumericData::kInt, 1.0, &status); - MAKE_OUTPUT(nAttr); - CHECK_MSTATUS ( nAttr.setDefault(1) ); - CHECK_MSTATUS ( addAttribute(mNumCubeLevelsX)); CHECK_MSTATUS ( addAttribute(mNumCubeLevelsY)); CHECK_MSTATUS ( addAttribute(mNumCubeLevelsZ)); @@ -519,10 +522,6 @@ MStatus MhFlowViewportAPILocator::initialize() CHECK_MSTATUS ( addAttribute(mCubeOpacity)); CHECK_MSTATUS ( addAttribute(mCubesUseInstancing)); CHECK_MSTATUS ( addAttribute(mCubesDeltaTrans)); - CHECK_MSTATUS ( addAttribute(mDummyInput)); - CHECK_MSTATUS ( addAttribute(mDummyOutput)); - - CHECK_MSTATUS ( attributeAffects(mDummyInput, mDummyOutput)); return status; } diff --git a/test/lib/mayaUsd/render/mayaToHydra/testFlowViewportAPI.py b/test/lib/mayaUsd/render/mayaToHydra/testFlowViewportAPI.py index 363697e85a..f07dbec139 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/testFlowViewportAPI.py +++ b/test/lib/mayaUsd/render/mayaToHydra/testFlowViewportAPI.py @@ -52,10 +52,6 @@ def test_AddingPrimitives(self): #Create a MhFlowViewportAPILocator node which adds a dataProducerSceneIndex and a Filtering scene index flowViewportNodeName = cmds.createNode("MhFlowViewportAPILocator") - self.assertFalse(flowViewportNodeName == None) - #When the node above is created, its compute method is not called automatically, so work around to trigger a call to compute - cmds.setAttr(flowViewportNodeName + '.dummyInput', 2)#setting this will set dirty the dummyOutput attribute - cmds.getAttr(flowViewportNodeName + '.dummyOutput')#getting this value will trigger a call to compute cmds.refresh() #Original images are located for example in maya-hydra\test\lib\mayaUsd\render\mayaToHydra\FlowViewportAPITest self.assertSnapshotClose("add_NodeCreated.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -63,11 +59,8 @@ def test_AddingPrimitives(self): #Move the transform node, the added prims (cube grid) should move as well # Get the transform node of the MhFlowViewportAPILocator transformNode = cmds.listRelatives(flowViewportNodeName, parent=True)[0] - self.assertFalse(transformNode == None) - #Select the transform node - cmds.select(transformNode) - # Move the selected node - cmds.move(10, 5, -5) + self.assertIsNotNone(transformNode) + cmds.move(10, 5, -5, transformNode) cmds.refresh() self.assertSnapshotClose("add_NodeMoved.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -96,9 +89,7 @@ def test_AddingPrimitives(self): self.assertSnapshotClose("add_NodeDeletedUndoAgain.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) #Move transform node again to see if it still updates the added prims transform - cmds.select(transformNode) - # Move the selected node - cmds.move(-20, -5, 0) + cmds.move(-20, -5, 0, transformNode) cmds.refresh() self.assertSnapshotClose("add_NodeMovedAfterDeletionAndUndo.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -111,7 +102,6 @@ def test_AddingPrimitives(self): #Finish by a File New command cmds.file(new=True, force=True) - """ #Test filtering primitives def test_FilteringPrimitives(self): self.setupScene() @@ -123,10 +113,6 @@ def test_FilteringPrimitives(self): #Create a MhFlowViewportAPILocator node which adds a dataProducerSceneIndex and a Filtering scene index flowViewportNodeName = cmds.createNode("MhFlowViewportAPILocator") - self.assertFalse(flowViewportNodeName == None) - #When the node above is created, its compute method is not called automatically, so work around to trigger a call to compute - cmds.setAttr(flowViewportNodeName + '.dummyInput', 3)#setting this will set dirty the dummyOutput attribute - cmds.getAttr(flowViewportNodeName + '.dummyOutput')#getting this value will trigger a call to compute cmds.refresh() #Original images are located for example in maya-hydra\test\lib\mayaUsd\render\mayaToHydra\FlowViewportAPITest self.assertSnapshotClose("filter_NodeCreated.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -134,11 +120,8 @@ def test_FilteringPrimitives(self): #Move the transform node, the added prims (cube grid) should move as well # Get the transform node of the MhFlowViewportAPILocator transformNode = cmds.listRelatives(flowViewportNodeName, parent=True)[0] - self.assertFalse(transformNode == None) - #Select the transform node - cmds.select(transformNode) - # Move the selected node - cmds.move(15, 0, 0) + self.assertIsNotNone(transformNode) + cmds.move(15, 0, 0, transformNode) cmds.refresh() self.assertSnapshotClose("filter_NodeMoved.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -205,11 +188,6 @@ def test_CubeGrid(self): #Create a MhFlowViewportAPILocator node which adds a dataProducerSceneIndex and a Filtering scene index flowViewportNodeName = cmds.createNode("MhFlowViewportAPILocator") - self.assertFalse(flowViewportNodeName == None) - - #When the node above is created, its compute method is not called automatically, so work around to trigger a call to compute - cmds.setAttr(flowViewportNodeName + '.dummyInput', 2)#setting this will set dirty the dummyOutput attribute - cmds.getAttr(flowViewportNodeName + '.dummyOutput')#getting this value will trigger a call to compute self.assertSnapshotClose("cubeGrid_BeforeModifs.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) #Get the matrix and set a rotation of 70 degress around Y axis. @@ -261,11 +239,6 @@ def test_MultipleNodes(self): #Create a MhFlowViewportAPILocator node which adds a dataProducerSceneIndex and a Filtering scene index flowViewportNodeName1 = cmds.createNode("MhFlowViewportAPILocator", n="nodeShape1") - self.assertFalse(flowViewportNodeName1 == None) - - #When the node above is created, its compute method is not called automatically, so work around to trigger a call to compute - cmds.setAttr(flowViewportNodeName1 + '.dummyInput', 2)#setting this will set dirty the dummyOutput attribute - cmds.getAttr(flowViewportNodeName1 + '.dummyOutput')#getting this value will trigger a call to compute #Get the matrix and set a rotation of 70 degress around Y axis. matrix = cmds.getAttr(flowViewportNodeName1 + '.cubeInitalTransform') @@ -287,20 +260,12 @@ def test_MultipleNodes(self): #Move the transform node, the added prims (cube grid) should move as well # Get the transform node of the MhFlowViewportAPILocator transformNode1 = cmds.listRelatives(flowViewportNodeName1, parent=True)[0] - self.assertFalse(transformNode1 == None) - #Select the transform node - cmds.select(transformNode1) - # Move the selected node - cmds.move(-10, 0, 0) + self.assertIsNotNone(transformNode1) + cmds.move(-10, 0, 0, transformNode1) cmds.refresh() #Create a MhFlowViewportAPILocator node which adds a dataProducerSceneIndex and a Filtering scene index flowViewportNodeName2 = cmds.createNode("MhFlowViewportAPILocator", n="nodeShape2") - self.assertFalse(flowViewportNodeName2 == None) - - #When the node above is created, its compute method is not called automatically, so work around to trigger a call to compute - cmds.setAttr(flowViewportNodeName2 + '.dummyInput', 3)#setting this will set dirty the dummyOutput attribute - cmds.getAttr(flowViewportNodeName2 + '.dummyOutput')#getting this value will trigger a call to compute #Get the matrix and set a rotation of 70 degress around Y axis. matrix = cmds.getAttr(flowViewportNodeName2 + '.cubeInitalTransform') @@ -322,11 +287,8 @@ def test_MultipleNodes(self): #Move the transform node, the added prims (cube grid) should move as well # Get the transform node of the MhFlowViewportAPILocator transformNode2 = cmds.listRelatives(flowViewportNodeName2, parent=True)[0] - self.assertFalse(transformNode2 == None) - #Select the transform node - cmds.select(transformNode2) - # Move the selected node - cmds.move(-30, 0, -30) + self.assertIsNotNone(transformNode2) + cmds.move(-30, 0, -30, transformNode2) cmds.refresh() self.assertSnapshotClose("multipleNodes_BeforeModifs.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -336,10 +298,9 @@ def test_MultipleNodes(self): cmds.setAttr(flowViewportNodeName2 + '.cubeOpacity', 0.1) # Apply transform on node #2 - cmds.select(transformNode2) - cmds.move(-30, 0, 0) - cmds.rotate(-30, 45, 0) - cmds.scale(2, 1, 1) + cmds.move(-30, 0, 0, transformNode2) + cmds.rotate(-30, 45, 0, transformNode2) + cmds.scale(2, 1, 1, transformNode2) cmds.refresh() self.assertSnapshotClose("multipleNodes_AfterModifs.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) @@ -381,19 +342,11 @@ def test_MultipleViewports(self): #Create a maya sphere sphereNode, sphereShape = cmds.polySphere() - #Select the transform node - cmds.select(sphereNode) - # Move the selected node - cmds.move(15, 0, 0) + cmds.move(15, 0, 0, sphereNode) cmds.refresh() #Create a MhFlowViewportAPILocator node which adds a dataProducerSceneIndex and a Filtering scene index flowViewportNodeName1 = cmds.createNode("MhFlowViewportAPILocator", n="nodeShape1") - self.assertFalse(flowViewportNodeName1 == None) - - #When the node above is created, its compute method is not called automatically, so work around to trigger a call to compute - cmds.setAttr(flowViewportNodeName1 + '.dummyInput', 2)#setting this will set dirty the dummyOutput attribute - cmds.getAttr(flowViewportNodeName1 + '.dummyOutput')#getting this value will trigger a call to compute #Modify the cube grid parameters cmds.setAttr(flowViewportNodeName1 + '.numCubesX', 3) @@ -446,6 +399,6 @@ def test_MultipleViewports(self): #Finish by a File New command cmds.file(new=True, force=True) - """ + if __name__ == '__main__': fixturesUtils.runTests(globals())