diff --git a/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.cpp b/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.cpp index 6631138a2a..25710df81b 100644 --- a/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.cpp +++ b/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.cpp @@ -96,7 +96,7 @@ bool FilteringSceneIndexInterfaceImp::_CreateSceneFilteringSceneIndicesData(cons std::lock_guard lock(sceneFilteringClient_mutex); auto findResult = std::find_if(sceneFilteringSceneIndicesData().cbegin(), sceneFilteringSceneIndicesData().cend(), - [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->getClient() == client;}); + [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->GetClient() == client;}); if (findResult != sceneFilteringSceneIndicesData().cend()){ return false; } @@ -123,7 +123,7 @@ bool FilteringSceneIndexInterfaceImp::_CreateSelectionHighlightFilteringSceneInd std::lock_guard lock(selectionHighlightFilteringClient_mutex); auto findResult = std::find_if(selectionHighlightFilteringSceneIndicesData.cbegin(), selectionHighlightFilteringSceneIndicesData.cend(), - [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->getClient() == client;}); + [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->GetClient() == client;}); if (findResult != selectionHighlightFilteringSceneIndicesData.cend()){ return false; } @@ -149,11 +149,11 @@ void FilteringSceneIndexInterfaceImp::_DestroySceneFilteringSceneIndicesData(con std::lock_guard lock(sceneFilteringClient_mutex); auto findResult = std::find_if(sceneFilteringSceneIndicesData().cbegin(), sceneFilteringSceneIndicesData().cend(), - [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->getClient() == client;}); + [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->GetClient() == client;}); if (findResult != sceneFilteringSceneIndicesData().cend()){ const auto& filteringSIData = (*findResult); rendererNames = (filteringSIData) - ? filteringSIData->getClient()->getRendererNames() + ? filteringSIData->GetClient()->getRendererNames() : FvpViewportAPITokens->allRenderers; sceneFilteringSceneIndicesData().erase(findResult);//This also decreases ref count @@ -175,7 +175,7 @@ void FilteringSceneIndexInterfaceImp::_DestroySelectionHighlightFilteringSceneIn std::lock_guard lock(selectionHighlightFilteringClient_mutex); auto findResult = std::find_if(selectionHighlightFilteringSceneIndicesData.cbegin(), selectionHighlightFilteringSceneIndicesData.cend(), - [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->getClient() == client;}); + [&client](const PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBaseRefPtr& filteringSIData) { return filteringSIData->GetClient() == client;}); if (findResult != selectionHighlightFilteringSceneIndicesData.cend()){ selectionHighlightFilteringSceneIndicesData.erase(findResult);//Also decreases ref count } diff --git a/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.h b/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.h index ad3bb3bdfc..b1c3b952e6 100644 --- a/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.h +++ b/lib/flowViewport/API/interfacesImp/fvpFilteringSceneIndexInterfaceImp.h @@ -46,8 +46,10 @@ class FilteringSceneIndexInterfaceImp : public FilteringSceneIndexInterface FVP_API void setSceneIndexDataFactory(FilteringSceneIndexDataAbstractFactory& factory); - //Called by Flow viewport + //Called by Flow viewport and the DCC + FVP_API const std::set& getSceneFilteringSceneIndicesData() const; + FVP_API const std::set& getSelectionHighlightFilteringSceneIndicesData() const; private : diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.cpp b/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.cpp index 87bb796045..a7fb6a8368 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.cpp +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.cpp @@ -44,7 +44,6 @@ namespace FVP_NS_DEF { */ DataProducerSceneIndexDataBase::DataProducerSceneIndexDataBase(const CreationParameters& params) { - _parentMatrix.SetIdentity(); _dataProducerSceneIndex = params._customDataProducerSceneIndex; _prefix = params._prefix; _lastSceneIndexChain = params._customDataProducerSceneIndex; @@ -55,7 +54,6 @@ DataProducerSceneIndexDataBase::DataProducerSceneIndexDataBase(const CreationPar //For Usd stages DataProducerSceneIndexDataBase::DataProducerSceneIndexDataBase(const CreationParametersForUsdStage& params) { - _parentMatrix.SetIdentity(); _dataProducerSceneIndex = nullptr;//Will be set later _prefix = params._prefix; _lastSceneIndexChain = nullptr;//Will be set later @@ -70,24 +68,6 @@ DataProducerSceneIndexDataBase::~DataProducerSceneIndexDataBase() #endif } -void DataProducerSceneIndexDataBase::UpdateHydraTransformFromParentPath() -{ - if (! _rootOverridesSceneIndex){ - return; - } - - _rootOverridesSceneIndex->SetRootTransform(_parentMatrix); -} - -void DataProducerSceneIndexDataBase::UpdateVisibilityFromDCCNode(bool isVisible) -{ - if (! _rootOverridesSceneIndex){ - return; - } - - _rootOverridesSceneIndex->SetRootVisibility(isVisible); -} - void DataProducerSceneIndexDataBase::_CreateSceneIndexChainForDataProducerSceneIndex() { if (_dccNode){ @@ -135,7 +115,6 @@ void DataProducerSceneIndexDataBase::_CreateSceneIndexChainForDataProducerSceneI { _rootOverridesSceneIndex = UsdImagingRootOverridesSceneIndex::New(inputSceneIndex); _lastSceneIndexChain = _rootOverridesSceneIndex; - UpdateHydraTransformFromParentPath();//Update the transform, this is useful when deleting the node and undoing it } void DataProducerSceneIndexDataBase::_CreateSceneIndexChainForDataProducerSceneIndexWithoutDCCNode(HdSceneIndexBaseRefPtr const & inputSceneIndex) diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.h b/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.h index fba5cb1114..ad5efc502f 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.h +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpDataProducerSceneIndexDataBase.h @@ -85,11 +85,11 @@ TF_DECLARE_WEAK_AND_REF_PTRS(DataProducerSceneIndexDataBase);//Be able to use Re const SdfPath& GetPrefix()const{return _prefix;} const std::string& GetRendererNames() const {return _rendererNames;} - /// Provide the node name from the DCC to be overriden in a DCC specific subclass - virtual std::string GetDCCNodeName() const {return "";} + // Returns true if visibility was changed, false otherwise. + virtual bool UpdateVisibility() = 0; - void UpdateVisibilityFromDCCNode(bool isVisible); - void UpdateHydraTransformFromParentPath(); + // Returns true if transform was changed, false otherwise. + virtual bool UpdateTransform() = 0; protected: @@ -119,9 +119,6 @@ TF_DECLARE_WEAK_AND_REF_PTRS(DataProducerSceneIndexDataBase);//Be able to use Re /// Is the last scene index of the scene index chain when a dccNode was passed. HdSceneIndexBaseRefPtr _lastSceneIndexChain = nullptr; - /// Is the world matrix of the scene index. It is used only when a dccNode was passed to override the transform of the root of the scene index. - GfMatrix4d _parentMatrix; - /// _rootOverridesSceneIndex is used to set a transform and visibility at the root of the Usd stage/data producer scene index. It is used only when a dccNode was passed. /// With this scene index when you select the proxyshape node and move it or hide it, we apply the same operation on the stage, same for a data producer scene index with a hosting node. UsdImagingRootOverridesSceneIndexRefPtr _rootOverridesSceneIndex = nullptr; diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.cpp b/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.cpp index 6fc96393e0..e8b0dcc202 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.cpp +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.cpp @@ -29,13 +29,6 @@ FilteringSceneIndexDataBase::FilteringSceneIndexDataBase(const std::shared_ptr<: { } -void FilteringSceneIndexDataBase::updateVisibilityFromDCCNode(bool isVisible) -{ - _isVisible = isVisible; - const std::string& rendererNames = _client->getRendererNames(); - ::Fvp::FilteringSceneIndicesChainManager::get().updateFilteringSceneIndicesChain(rendererNames); -} - }//End of namespace FVP_NS_DEF PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.h b/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.h index b58e8bda6b..15082e910a 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.h +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndexDataBase.h @@ -41,10 +41,12 @@ TF_DECLARE_WEAK_AND_REF_PTRS(FilteringSceneIndexDataBase);//Be able to use Ref c ~FilteringSceneIndexDataBase() override = default; - void updateVisibilityFromDCCNode(bool isVisible); - std::shared_ptr<::Fvp::FilteringSceneIndexClient> getClient() {return _client;} - bool getVisible() const{return _isVisible;} - void setVisible(bool visible) {_isVisible = visible;} + std::shared_ptr<::Fvp::FilteringSceneIndexClient> GetClient() {return _client;} + + bool GetVisibility() { return _isVisible; } + + // Returns true if visibility was changed, false otherwise. + virtual bool UpdateVisibility() = 0; protected: FilteringSceneIndexDataBase(const std::shared_ptr<::Fvp::FilteringSceneIndexClient>& filteringSIClient); @@ -53,7 +55,7 @@ TF_DECLARE_WEAK_AND_REF_PTRS(FilteringSceneIndexDataBase);//Be able to use Ref c const std::shared_ptr<::Fvp::FilteringSceneIndexClient> _client; ///_isVisible is true when the filteringSceneIndices should be visible and false when they are not such as when the hosting node has been hidden/deleted. - bool _isVisible = true; + bool _isVisible = false; }; }//End of namespace FVP_NS_DEF { diff --git a/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndicesChainManager.cpp b/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndicesChainManager.cpp index a50aa1ceb3..1c3050ac65 100644 --- a/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndicesChainManager.cpp +++ b/lib/flowViewport/API/perViewportSceneIndicesData/fvpFilteringSceneIndicesChainManager.cpp @@ -141,7 +141,7 @@ void FilteringSceneIndicesChainManager::_AppendFilteringSceneIndicesChain( View //Call our Hydra viewport API mechanism for custom filtering scene index clients const auto& viewportFilteringSceneIndicesData = FilteringSceneIndexInterfaceImp::get().getSceneFilteringSceneIndicesData(); for (const auto& filteringSceneIndexData : viewportFilteringSceneIndicesData) { - auto client = filteringSceneIndexData->getClient(); + auto client = filteringSceneIndexData->GetClient(); const std::string& rendererNames = client->getRendererNames(); //Filter by render delegate name if ( (FvpViewportAPITokens->allRenderers != rendererNames) && rendererNames.find(rendererDisplayName) == std::string::npos){ @@ -149,7 +149,7 @@ void FilteringSceneIndicesChainManager::_AppendFilteringSceneIndicesChain( View continue; } - const bool isVisible = filteringSceneIndexData->getVisible(); + const bool isVisible = filteringSceneIndexData->GetVisibility(); if (! isVisible){ continue; //We should not append not visible filtering scene indices } diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.cpp index d5cff83d4d..80a83be2ab 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.cpp @@ -23,156 +23,168 @@ #include "mayaHydraLib/hydraUtils.h" #include "mayaHydraLib/mayaUtils.h" -//Maya headers -#include -#include -#include -#include -#include -#include - -namespace -{ - //Callback when an attribute of a maya node changes - void attributeChangedCallback(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug & otherPlug, void* mayaDataProducerSceneIndexData) - { - if( ! mayaDataProducerSceneIndexData){ - return; - } - - //We care only about transform and visibility attributes - MFnAttribute attr (plug.attribute()); - if (MayaHydra::IsAMayaTransformAttributeName(attr.name())){ - PXR_NS::MayaDataProducerSceneIndexData* MayaDataProducerSceneIndexData = (PXR_NS::MayaDataProducerSceneIndexData*)mayaDataProducerSceneIndexData; - MayaDataProducerSceneIndexData->UpdateTransformFromMayaNode(); - } else{ - bool isVisible = false; - if (MayaHydra::IsAMayaVisibilityAttribute(plug, isVisible)){ - PXR_NS::MayaDataProducerSceneIndexData* MayaDataProducerSceneIndexData = (PXR_NS::MayaDataProducerSceneIndexData*)mayaDataProducerSceneIndexData; - MayaDataProducerSceneIndexData->UpdateVisibilityFromDCCNode(isVisible); - } - } - } +PXR_NAMESPACE_USING_DIRECTIVE - //Callback when a maya node is added - void onDagNodeAdded(MObject& obj, void* data) +class MayaDataProducerSceneIndexData::UfeSceneChangesHandler : public Ufe::Observer +{ +public: + UfeSceneChangesHandler(MayaDataProducerSceneIndexData& dataProducer) + : _dataProducer(dataProducer) { - //Since when an object is recreated (when doing an undo after a delete of the node), the MObject from the same node are different but their MObjectHandle::hashCode() are identical so - //this is how we identify which object we need to deal with. - PXR_NS::MayaDataProducerSceneIndexData* mayaDataProducerSceneIndexData = (PXR_NS::MayaDataProducerSceneIndexData*)data; - const MObjectHandle objHandle(obj); - if(mayaDataProducerSceneIndexData && mayaDataProducerSceneIndexData->getObjHandle().isValid() && objHandle.hashCode() == mayaDataProducerSceneIndexData->getObjHandle().hashCode()){ - mayaDataProducerSceneIndexData->UpdateVisibilityFromDCCNode(true); - } } - //Callback when a maya node is removed - void onDagNodeRemoved(MObject& obj, void* data) - { - //Since when an object is recreated (when doing an undo after a delete of the node), the MObject from the same node are different but their MObjectHandle::hashCode() are identical so - //this is how we identify which object we need to deal with. - PXR_NS::MayaDataProducerSceneIndexData* mayaDataProducerSceneIndexData = (PXR_NS::MayaDataProducerSceneIndexData*)data; - const MObjectHandle objHandle(obj); - if(mayaDataProducerSceneIndexData && mayaDataProducerSceneIndexData->getObjHandle().isValid() && objHandle.hashCode() == mayaDataProducerSceneIndexData->getObjHandle().hashCode()){ - mayaDataProducerSceneIndexData->UpdateVisibilityFromDCCNode(false); - } - } -} + void operator()(const Ufe::Notification& notification) override; + void handleSceneChanged(const Ufe::SceneChanged& sceneChanged); -PXR_NAMESPACE_USING_DIRECTIVE +private: + MayaDataProducerSceneIndexData& _dataProducer; +}; MayaDataProducerSceneIndexData::MayaDataProducerSceneIndexData(const FVP_NS_DEF::DataProducerSceneIndexDataBase::CreationParameters& params) : FVP_NS_DEF::DataProducerSceneIndexDataBase(params) { - CreateNodeCallbacks(); + if (_dccNode) { + SetupUfeObservation(_dccNode); + } _CreateSceneIndexChainForDataProducerSceneIndex(); } MayaDataProducerSceneIndexData::MayaDataProducerSceneIndexData(FVP_NS_DEF::DataProducerSceneIndexDataBase::CreationParametersForUsdStage& params) : FVP_NS_DEF::DataProducerSceneIndexDataBase(params) { - CreateNodeCallbacks(); + if (_dccNode) { + SetupUfeObservation(_dccNode); + } _CreateSceneIndexChainForUsdStageSceneIndex(params); } -void MayaDataProducerSceneIndexData::CreateNodeCallbacks() -{ - //When the user has passed a maya node we are adding callbacks on various changes to be able to act on the data producer scene index prims automatically - if (_dccNode){ - MObject* mObj = reinterpret_cast(_dccNode); - _mObjHandle = MObjectHandle(*mObj); - - _mayaNodeDagPath = MDagPath::getAPathTo(*mObj); - - _CopyMayaNodeTransform();//Copy it so that the classes created in _CreateSceneIndexChainForDataProducerSceneIndex have the up to date matrix - - MCallbackId cbId = MNodeMessage::addAttributeChangedCallback(*mObj, attributeChangedCallback, this); - if (cbId){ - _nodeMessageCallbackIds.append(cbId); - } - - //Also monitor parent DAG node to be able to update the scene index if the parent transform is modified or the visibility changed - MDagPath parentDagPath = _mayaNodeDagPath; - parentDagPath.pop(); - MObject parentObj = parentDagPath.node(); - cbId = 0; - cbId = MNodeMessage::addAttributeChangedCallback(parentObj, attributeChangedCallback, this); - if (cbId){ - _nodeMessageCallbackIds.append(cbId); - } - - //Get node type name to filter by node type for callbacks - MFnDependencyNode dep(*mObj); - const MString nodeTypeName = dep.typeName(); - - //Setup node added callback, filter by node type using nodeTypeName - cbId = 0; - cbId = MDGMessage::addNodeAddedCallback(onDagNodeAdded, nodeTypeName, this); - if (cbId) { - _dGMessageCallbackIds.append(cbId); - } - - //Setup node remove callback, filter by node type using nodeTypeName - cbId = 0; - cbId = MDGMessage::addNodeRemovedCallback(onDagNodeRemoved, nodeTypeName, this); - if (cbId) { - _dGMessageCallbackIds.append(cbId); - } +MayaDataProducerSceneIndexData::~MayaDataProducerSceneIndexData() +{ + if (_ufeSceneChangesHandler) { + Ufe::Scene::instance().removeObserver(_ufeSceneChangesHandler); + _ufeSceneChangesHandler.reset(); } } -MayaDataProducerSceneIndexData::~MayaDataProducerSceneIndexData() +void MayaDataProducerSceneIndexData::SetupUfeObservation(void* dccNode) { - CHECK_MSTATUS(MNodeMessage::removeCallbacks (_nodeMessageCallbackIds)); - CHECK_MSTATUS(MMessage::removeCallbacks (_dGMessageCallbackIds)); + if (dccNode) { + MObject* mObject = reinterpret_cast(dccNode); + MDagPath dagPath; + MDagPath::getAPathTo(*mObject, dagPath); + dagPath.extendToShape(); + + _path = Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath)); + + _ufeSceneChangesHandler = std::make_shared(*this); + Ufe::Scene::instance().addObserver(_ufeSceneChangesHandler); + + // Note : while we currently use a query-based approach to update the transform and visibility, + // we could also move to a UFE notifications-based approach if necessary. In this case, we would + // setup the subject-observer relationships here. + // For visibility changes, the observer would observe the Ufe::Object3d subject, receive + // Ufe::VisibilityChanged notifications and call UpdateVisibility() if the received notification + // is relevant (i.e. if the data producer's path starts with the notification's path, the + // same way as in MayaDataProducerSceneIndexData::UfeSceneChangesHandler::operator()). + // For transform changes, the observer would observe a Ufe::Transform3dPathSubject created off the + // UFE path of the node (_path), receive Ufe::Transform3dChanged notifications and call UpdateTransform() + // if the received notification is relevant (i.e. if the data producer's path starts with the notification's + // path, the same way as in MayaDataProducerSceneIndexData::UfeSceneChangesHandler::operator()). + } } -void MayaDataProducerSceneIndexData::UpdateTransformFromMayaNode() -{ - _CopyMayaNodeTransform(); - UpdateHydraTransformFromParentPath(); +bool MayaDataProducerSceneIndexData::UpdateVisibility() +{ + if (!_path.has_value()) { + return false; + } + // Having a UFE path means we have an associated DCC node, + // so we should also have a UsdImagingRootOverridesSceneIndex + TF_AXIOM(_rootOverridesSceneIndex); + + bool isVisible = true; + Ufe::Path currPath = _path.value(); + while (isVisible && !currPath.empty()) { + auto sceneItem = Ufe::Hierarchy::createItem(currPath); + auto object3d = Ufe::Object3d::object3d(sceneItem); + isVisible = isVisible && object3d != nullptr && object3d->visibility(); + currPath = currPath.pop(); + } + if (_rootOverridesSceneIndex->GetRootVisibility() != isVisible) { + _rootOverridesSceneIndex->SetRootVisibility(isVisible); + return true; + } + return false; } -void MayaDataProducerSceneIndexData::_CopyMayaNodeTransform() -{ - if (_mayaNodeDagPath.isValid()){ - //Get Maya transform value - //Convert from Maya matrix to GfMatrix4d - const MMatrix mayaMat = _mayaNodeDagPath.inclusiveMatrix(); - - //Copy Maya matrix into _parentMatrix member of this struct - memcpy(_parentMatrix.GetArray(), mayaMat[0], sizeof(double) * 16); +bool MayaDataProducerSceneIndexData::UpdateTransform() +{ + if (!_path.has_value()) { + return false; } + // Having a UFE path means we have an associated DCC node, + // so we should also have a UsdImagingRootOverridesSceneIndex + TF_AXIOM(_rootOverridesSceneIndex); + + auto transform = Ufe::Transform3dRead::transform3dRead(Ufe::Hierarchy::createItem(_path.value())); + if (!transform) { + return false; + } + GfMatrix4d transformMatrix; + std::memcpy( + transformMatrix.GetArray(), + transform->inclusiveMatrix().matrix.data(), + sizeof(decltype(transformMatrix)::ScalarType) * transformMatrix.numRows * transformMatrix.numColumns); + if (!GfIsClose(_rootOverridesSceneIndex->GetRootTransform(), transformMatrix, 1e-9)) { + _rootOverridesSceneIndex->SetRootTransform(transformMatrix); + return true; + } + return false; } -std::string MayaDataProducerSceneIndexData::GetDCCNodeName() const +void MayaDataProducerSceneIndexData::UfeSceneChangesHandler::operator()(const Ufe::Notification& notification) { - std::string outNodeName; - if (_mObjHandle.isValid() && _mObjHandle.isAlive()){ - MFnDependencyNode dep(_mObjHandle.object()); - outNodeName = std::string(dep.name().asChar()); - MAYAHYDRA_NS_DEF::SanitizeNameForSdfPath(outNodeName); + // We're processing UFE notifications, which implies that a path must be in use. + TF_AXIOM(_dataProducer._path.has_value()); + + const Ufe::SceneChanged& sceneChangedNotif = notification.staticCast(); + if (_dataProducer._path.value().startsWith(sceneChangedNotif.changedPath())) { + handleSceneChanged(sceneChangedNotif); } +} + +void MayaDataProducerSceneIndexData::UfeSceneChangesHandler::handleSceneChanged(const Ufe::SceneChanged& sceneChanged) +{ + auto handleSingleOperation = [&](const Ufe::SceneCompositeNotification::Op& sceneOperation) -> void { + // We're processing UFE notifications, which implies that a path must be in use. + TF_AXIOM(_dataProducer._path.has_value()); + // Having a UFE path means we have an associated DCC node, + // so we should also have a UsdImagingRootOverridesSceneIndex + TF_AXIOM(_dataProducer._rootOverridesSceneIndex); + + if (!_dataProducer._path.value().startsWith(sceneOperation.path)) { + // This notification does not relate to our parent hierarchy, so we have nothing to do. + return; + } - return outNodeName; + if (sceneOperation.opType == Ufe::SceneChanged::ObjectPathChange) { + switch (sceneOperation.subOpType) { + case Ufe::ObjectPathChange::ObjectRename: + _dataProducer._path = _dataProducer._path.value().replaceComponent( + sceneOperation.item->path().size() - 1, sceneOperation.item->path().back()); + break; + case Ufe::ObjectPathChange::ObjectReparent: + _dataProducer._path = _dataProducer._path.value().reparent(sceneOperation.path, sceneOperation.item->path()); + break; + } + } + }; + if (sceneChanged.opType() == Ufe::SceneChanged::SceneCompositeNotification) { + const auto& compositeNotification = sceneChanged.staticCast(); + for (const auto& operation : compositeNotification) { + handleSingleOperation(operation); + } + } else { + handleSingleOperation(sceneChanged); + } } diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.h index 0dc9467a75..a8f22084ad 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaDataProducerSceneIndexData.h @@ -19,11 +19,13 @@ //Flow viewport headers #include -//Maya headers -#include -#include -#include -#include +// UFE headers +#include "ufeExtensions/Global.h" +#include +#include +#include +#include +#include PXR_NAMESPACE_OPEN_SCOPE @@ -50,32 +52,22 @@ TF_DECLARE_WEAK_AND_REF_PTRS(MayaDataProducerSceneIndexData);//Be able to use Re ///Destructor ~MayaDataProducerSceneIndexData() override; - /// Get the MObject handle - const MObjectHandle& getObjHandle()const {return _mObjHandle;} + bool UpdateVisibility() override; + bool UpdateTransform() override; - /// Provide the node name from maya - std::string GetDCCNodeName() const override; - - ///Update transform from maya node - void UpdateTransformFromMayaNode(); - private: MayaDataProducerSceneIndexData(const FVP_NS_DEF::DataProducerSceneIndexDataBase::CreationParameters& params); MayaDataProducerSceneIndexData(FVP_NS_DEF::DataProducerSceneIndexDataBase::CreationParametersForUsdStage& params); - void CreateNodeCallbacks(); - void _CopyMayaNodeTransform(); + void SetupUfeObservation(void* dccNode); - //The following members are optional and used only when a dccNode was passed in the constructor of DataProducerSceneIndexDataBase - - /// Is the MObjectHandle of the maya node shape, it may be invalid if no maya node MObject pointer was passed. - MObjectHandle _mObjHandle; - /// Is the dag path of the Maya node passed in AppendSceneIndex. It is used only when a dccNode was passed. - MDagPath _mayaNodeDagPath; - /// Are the callbacks Ids set in maya to forward the changes done in maya on the data producer scene index. - MCallbackIdArray _nodeMessageCallbackIds; - /// Are the callbacks Ids set in maya to handle delete and deletion undo/redo - MCallbackIdArray _dGMessageCallbackIds; + // Path to the scene item, if it was added as one + std::optional _path; + + // To observe scene changes to the data producer scene item, if it exists + Ufe::Observer::Ptr _ufeSceneChangesHandler; + + class UfeSceneChangesHandler; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.cpp index 6658494e85..8ae8430ab3 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.cpp @@ -17,111 +17,138 @@ //Local headers #include "mayaHydraMayaFilteringSceneIndexData.h" +#include "mayaHydraLib/hydraUtils.h" #include "mayaHydraLib/mayaUtils.h" //flow viewport headers #include +#include -//Maya headers -#include -#include -#include -#include -#include -#include +PXR_NAMESPACE_USING_DIRECTIVE -namespace +class MayaFilteringSceneIndexData::UfeSceneChangesHandler : public Ufe::Observer { - //Callback when an attribute of this a Maya node changes - void attributeChangedCallback(MNodeMessage::AttributeMessage msg, MPlug& plug, MPlug& otherPlug, void* data) +public: + UfeSceneChangesHandler(MayaFilteringSceneIndexData& filteringData) + : _filteringData(filteringData) { - if( ! data){ - return; - } - - bool isVisible = false; - if (MayaHydra::IsAMayaVisibilityAttribute(plug, isVisible)){ - PXR_NS::MayaFilteringSceneIndexData* mayaFilteringSceneIndexData = (PXR_NS::MayaFilteringSceneIndexData*)data; - mayaFilteringSceneIndexData->updateVisibilityFromDCCNode(isVisible); - } } - //Callback when a node is added - void onDagNodeAdded(MObject& obj, void* data) - { - //Since when an object is recreated (such as doing an undo after a delete of the node), the MObject are different but their MObjectHandle::hasCode() are identical so - //this is how we identify which object we need to deal with. - PXR_NS::MayaFilteringSceneIndexData* mayaFilteringSceneIndexData = (PXR_NS::MayaFilteringSceneIndexData*)data; - const MObjectHandle objHandle(obj); - if(mayaFilteringSceneIndexData&& mayaFilteringSceneIndexData->getObjHandle().isValid() && objHandle.hashCode() == mayaFilteringSceneIndexData->getObjHandle().hashCode()){ - mayaFilteringSceneIndexData->updateVisibilityFromDCCNode(true); - } - } - - //Callback when a node is removed - void onDagNodeRemoved(MObject& obj, void* data) - { - //Since when an object is recreated (such as doing an undo after a delete of the node), the MObject are different but their MObjectHandle::hasCode() are identical so - //this is how we identify which object we need to deal with. - PXR_NS::MayaFilteringSceneIndexData* mayaFilteringSceneIndexData = (PXR_NS::MayaFilteringSceneIndexData*)data; - const MObjectHandle objHandle(obj); - if(mayaFilteringSceneIndexData&& mayaFilteringSceneIndexData->getObjHandle().isValid() && objHandle.hashCode() == mayaFilteringSceneIndexData->getObjHandle().hashCode()){ - mayaFilteringSceneIndexData->updateVisibilityFromDCCNode(false); - } - } -} + void operator()(const Ufe::Notification& notification) override; + void handleSceneChanged(const Ufe::SceneChanged& sceneChanged); -PXR_NAMESPACE_USING_DIRECTIVE +private: + MayaFilteringSceneIndexData& _filteringData; +}; MayaFilteringSceneIndexData::MayaFilteringSceneIndexData(const std::shared_ptr<::FVP_NS_DEF::FilteringSceneIndexClient>& client) : PXR_NS::FVP_NS_DEF::FilteringSceneIndexDataBase(client) { - //If a maya node is present in client.getDccNode(), add callbacks to handle node deleted/undo/redo and hide/unhide void* dccNode = client->getDccNode(); - if (dccNode){ - MObject* mObj = reinterpret_cast(dccNode); - _mObjHandle = MObjectHandle(*mObj); - - MCallbackId cbId = MNodeMessage::addAttributeChangedCallback(*mObj, attributeChangedCallback, this); - if (cbId){ - _nodeMessageCallbackIds.append(cbId); - } + if (dccNode) { + SetupUfeObservation(dccNode); + } +} - const MDagPath mayaNodeDagPath = MDagPath::getAPathTo(*mObj); - - //Also monitor parent DAG node to be able to update the scene index if the visibility is modified - MDagPath parentDagPath = mayaNodeDagPath; - parentDagPath.pop(); - MObject parentObj = parentDagPath.node(); - cbId = 0; - cbId = MNodeMessage::addAttributeChangedCallback(parentObj, attributeChangedCallback, this); - if (cbId){ - _nodeMessageCallbackIds.append(cbId); - } +MayaFilteringSceneIndexData::~MayaFilteringSceneIndexData() +{ + if (_ufeSceneChangesHandler) { + Ufe::Scene::instance().removeObserver(_ufeSceneChangesHandler); + _ufeSceneChangesHandler.reset(); + } +} - //Get node type name to filter by node type for callbacks - MFnDependencyNode dep(*mObj); - const MString nodeTypeName = dep.typeName(); +void MayaFilteringSceneIndexData::SetupUfeObservation(void* dccNode) +{ + if (dccNode) { + MObject* mObject = reinterpret_cast(dccNode); + MDagPath dagPath; + MDagPath::getAPathTo(*mObject, dagPath); + dagPath.extendToShape(); + + _path = Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath)); + + _ufeSceneChangesHandler = std::make_shared(*this); + Ufe::Scene::instance().addObserver(_ufeSceneChangesHandler); + + // Note : while we currently use a query-based approach to update the visibility, + // we could also move to a UFE notifications-based approach if necessary. In this case, + // we would setup the subject-observer relationships here. The observer would observe + // the Ufe::Object3d subject, receive Ufe::VisibilityChanged notifications and call + // UpdateVisibility() if the received notification is relevant (i.e. if the filtering + // scene index's path starts with the notification's path, the same way as in + // MayaFilteringSceneIndexData::UfeSceneChangesHandler::operator()). + } +} - //Setup node added callback, filter by node type using nodeTypeName - cbId = 0; - cbId = MDGMessage::addNodeAddedCallback(onDagNodeAdded, nodeTypeName, this); - if (cbId) { - _dGMessageCallbackIds.append(cbId); - } +bool MayaFilteringSceneIndexData::UpdateVisibility() +{ + if (!_path.has_value()) { + return false; + } - //Setup node remove callback, filter by node type using nodeTypeName - cbId = 0; - cbId = MDGMessage::addNodeRemovedCallback(onDagNodeRemoved, nodeTypeName, this); - if (cbId) { - _dGMessageCallbackIds.append(cbId); - } + bool isVisible = true; + Ufe::Path currPath = _path.value(); + while (isVisible && !currPath.empty()) { + auto sceneItem = Ufe::Hierarchy::createItem(currPath); + auto object3d = Ufe::Object3d::object3d(sceneItem); + isVisible = isVisible && object3d != nullptr && object3d->visibility(); + currPath = currPath.pop(); + } + if (_isVisible != isVisible) { + _isVisible = isVisible; + return true; } + return false; } -MayaFilteringSceneIndexData::~MayaFilteringSceneIndexData() +void MayaFilteringSceneIndexData::UfeSceneChangesHandler::operator()( + const Ufe::Notification& notification) { - MNodeMessage::removeCallbacks (_nodeMessageCallbackIds); - MMessage::removeCallbacks (_dGMessageCallbackIds); + // We're processing UFE notifications, which implies that a path must be in use. + TF_AXIOM(_filteringData._path.has_value()); + + const Ufe::SceneChanged& sceneChangedNotif = notification.staticCast(); + if (_filteringData._path.value().startsWith(sceneChangedNotif.changedPath())) { + handleSceneChanged(sceneChangedNotif); + } } +void MayaFilteringSceneIndexData::UfeSceneChangesHandler::handleSceneChanged( + const Ufe::SceneChanged& sceneChanged) +{ + auto handleSingleOperation + = [&](const Ufe::SceneCompositeNotification::Op& sceneOperation) -> void { + // We're processing UFE notifications, which implies that a path must be in use. + TF_AXIOM(_filteringData._path.has_value()); + + if (!_filteringData._path.value().startsWith(sceneOperation.path)) { + // This notification does not relate to our parent hierarchy, so we have nothing to do. + return; + } + + if (sceneOperation.opType == Ufe::SceneChanged::ObjectPathChange) { + switch (sceneOperation.subOpType) { + case Ufe::ObjectPathChange::ObjectRename: { + _filteringData._path = _filteringData._path.value().replaceComponent( + sceneOperation.item->path().size() - 1, sceneOperation.item->path().back()); + break; + } + case Ufe::ObjectPathChange::ObjectReparent: { + _filteringData._path = _filteringData._path.value().reparent( + sceneOperation.path, sceneOperation.item->path()); + break; + } + } + } + }; + if (sceneChanged.opType() == Ufe::SceneChanged::SceneCompositeNotification) { + const auto& compositeNotification + = sceneChanged.staticCast(); + for (const auto& operation : compositeNotification) { + handleSingleOperation(operation); + } + } else { + handleSingleOperation(sceneChanged); + } +} diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.h index 0ecb3c06b9..507eb227fb 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMayaFilteringSceneIndexData.h @@ -21,10 +21,12 @@ #include #include -//Maya headers -#include -#include -#include +// UFE headers +#include "ufeExtensions/Global.h" +#include +#include +#include +#include PXR_NAMESPACE_OPEN_SCOPE @@ -43,21 +45,20 @@ TF_DECLARE_WEAK_AND_REF_PTRS(MayaFilteringSceneIndexData);//Be able to use Ref c ~MayaFilteringSceneIndexData() override; - const MObjectHandle& getObjHandle()const {return _mObjHandle;} - + bool UpdateVisibility() override; + private: MayaFilteringSceneIndexData(const std::shared_ptr<::FVP_NS_DEF::FilteringSceneIndexClient>& client); - ///The following members are optional and used only when a dccNode was passed in the FilteringSceneIndexClient - - /// Is the MObjectHandle of the maya node shape, it may be invalid if no maya node MObject pointer was passed in the FilteringSceneIndexClient. - MObjectHandle _mObjHandle; - - /// Are the callbacks Ids set in maya to forward the changes done in maya on the dataProducer scene index. - MCallbackIdArray _nodeMessageCallbackIds; + void SetupUfeObservation(void* dccNode); + + // Path to the scene item, if it was added as one + std::optional _path; + + // To observe scene changes to the scene item, if it exists + Ufe::Observer::Ptr _ufeSceneChangesHandler; - /// Are the callbacks Ids set in maya to handle delete and deletion undo/redo - MCallbackIdArray _dGMessageCallbackIds; + class UfeSceneChangesHandler; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index 53315d9aa1..6be5e8c69a 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -42,8 +42,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -993,6 +995,30 @@ MStatus MtohRenderOverride::Render( } } + // Update plugin data producers + for (auto& viewportData : Fvp::ViewportInformationAndSceneIndicesPerViewportDataManager::Get().GetAllViewportInfoAndData()) { + for (auto& dataProducer : viewportData.GetDataProducerSceneIndicesData()) { + dataProducer->UpdateVisibility(); + dataProducer->UpdateTransform(); + } + } + + // Update plugin filtering scene indices + std::string rendererNamesToUpdate; + for (auto& sceneFilteringSceneIndexData : Fvp::FilteringSceneIndexInterfaceImp::get().getSceneFilteringSceneIndicesData()) { + if (sceneFilteringSceneIndexData->UpdateVisibility()) { + rendererNamesToUpdate += sceneFilteringSceneIndexData->GetClient()->getRendererNames(); + } + } + for (auto& selectionHighlightFilteringSceneIndexData : Fvp::FilteringSceneIndexInterfaceImp::get().getSelectionHighlightFilteringSceneIndicesData()) { + if (selectionHighlightFilteringSceneIndexData->UpdateVisibility()) { + rendererNamesToUpdate += selectionHighlightFilteringSceneIndexData->GetClient()->getRendererNames(); + } + } + if (!rendererNamesToUpdate.empty()) { + Fvp::FilteringSceneIndicesChainManager::get().updateFilteringSceneIndicesChain(rendererNamesToUpdate); + } + _engine.Execute(_renderIndex, &tasks); // HdTaskController will query all of the tasks it can for IsConverged. diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index 1b88840cea..7b9d31808b 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -47,6 +47,7 @@ set(INTERACTIVE_TEST_SCRIPT_FILES testPassingNormalsOnMayaNative.py testViewportFilters.py testMayaComponentsPicking.py + testFlowPluginsHierarchicalProperties.py cpp/testColorPreferences.py cpp/testCppFramework.py cpp/testMayaSceneFlattening.py diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_parentTransformChanged.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_parentTransformChanged.png new file mode 100644 index 0000000000..a0b779edf9 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_parentTransformChanged.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_shapeTransformChanged.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_shapeTransformChanged.png new file mode 100644 index 0000000000..50e75dc83a Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_shapeTransformChanged.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_visibility_off.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_visibility_off.png new file mode 100644 index 0000000000..48fdd6bda2 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_visibility_off.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_visibility_on.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_visibility_on.png new file mode 100644 index 0000000000..58a62387b8 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/authoring_locator_visibility_on.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_hidden.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_hidden.png new file mode 100644 index 0000000000..48fdd6bda2 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_hidden.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_initial.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_initial.png new file mode 100644 index 0000000000..58a62387b8 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_initial.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_translated.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_translated.png new file mode 100644 index 0000000000..42df4ee797 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/locator_playback_translated.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t0.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t0.png new file mode 100644 index 0000000000..befa959f2a Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t0.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t1.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t1.png new file mode 100644 index 0000000000..f49f20cccf Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t1.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t10.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t10.png new file mode 100644 index 0000000000..28e49e6743 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t10.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t12.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t12.png new file mode 100644 index 0000000000..28e49e6743 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t12.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t15.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t15.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t15.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t3.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t3.png new file mode 100644 index 0000000000..1c3521e8be Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t3.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t5.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t5.png new file mode 100644 index 0000000000..392c826e1a Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t5.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t7.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t7.png new file mode 100644 index 0000000000..663ffb5966 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStageAnimatedPrim_t7.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_parentTransformChanged.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_parentTransformChanged.png new file mode 100644 index 0000000000..82e4e7d9d4 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_parentTransformChanged.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_hidden.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_hidden.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_hidden.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_initial.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_initial.png new file mode 100644 index 0000000000..bcacb0d864 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_initial.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_translated.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_translated.png new file mode 100644 index 0000000000..f66390edd1 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_playback_translated.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_shapeTransformChanged.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_shapeTransformChanged.png new file mode 100644 index 0000000000..61a2f68dcf Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_shapeTransformChanged.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_visibility_off.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_visibility_off.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_visibility_off.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_visibility_on.png b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_visibility_on.png new file mode 100644 index 0000000000..bcacb0d864 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowPluginsHierarchicalPropertiesTest/usdStage_visibility_on.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_locator_visibility_off.png b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_locator_visibility_off.png new file mode 100644 index 0000000000..48fdd6bda2 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_locator_visibility_off.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_locator_visibility_on.png b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_locator_visibility_on.png new file mode 100644 index 0000000000..20dc7c31b9 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_locator_visibility_on.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_parentTransformChanged.png b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_parentTransformChanged.png new file mode 100644 index 0000000000..82e4e7d9d4 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_parentTransformChanged.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_shapeTransformChanged.png b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_shapeTransformChanged.png new file mode 100644 index 0000000000..61a2f68dcf Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_shapeTransformChanged.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_visibility_off.png b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_visibility_off.png new file mode 100644 index 0000000000..dbedef32fd Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_visibility_off.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_visibility_on.png b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_visibility_on.png new file mode 100644 index 0000000000..bcacb0d864 Binary files /dev/null and b/test/lib/mayaUsd/render/mayaToHydra/FlowViewportAPITest/hierarchicalProperties_usdStage_visibility_on.png differ diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIAddPrims.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIAddPrims.cpp index 8b1cc5a39f..396bc0bf90 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIAddPrims.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIAddPrims.cpp @@ -26,6 +26,7 @@ #include //maya headers +#include #include #include #include @@ -107,13 +108,19 @@ TEST(FlowViewportAPI, addPrimitives) MPlug visibilityPlug = depNode.findPlug("visibility"); ASSERT_FALSE(visibilityPlug.isNull()); visibilityPlug.setBool(false); - + + // Refresh to update the data producer transform & visibility + M3dView::active3dView().refresh(); + foundPrims = inspector.FindPrims(findFirstCubePrimPredicate, 1); ASSERT_EQ(foundPrims.size(), 0u);//The cube should not be found //Unhide the shape node visibilityPlug.setBool(true); - + + // Refresh to update the data producer transform & visibility + M3dView::active3dView().refresh(); + foundPrims = inspector.FindPrims(findFirstCubePrimPredicate, 1); ASSERT_EQ(foundPrims.size(), 1u);//The cube should be found diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIFilterPrims.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIFilterPrims.cpp index cfd69f6d6b..b64cf02d23 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIFilterPrims.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testFlowViewportAPIFilterPrims.cpp @@ -26,6 +26,7 @@ #include //maya headers +#include #include #include #include @@ -96,6 +97,9 @@ TEST(FlowViewportAPI, filterPrimitives) PrimEntriesVector foundPrims = inspector.FindPrims(smallSpherePredicate, 1); ASSERT_EQ(foundPrims.size(), 1u); //The small sphere should be found and visible + // Refresh to update the filtering scene index chain + M3dView::active3dView().refresh(false, true); + foundPrims = inspector.FindPrims(bigSpherePredicate, 1); ASSERT_EQ(foundPrims.size(), 0u); //The big sphere should be filtered (not visible) @@ -105,13 +109,19 @@ TEST(FlowViewportAPI, filterPrimitives) MPlug visibilityPlug = depNode.findPlug("visibility"); ASSERT_FALSE(visibilityPlug.isNull()); visibilityPlug.setBool(false); - + + // Refresh to update the filtering scene index chain + M3dView::active3dView().refresh(false, true); + foundPrims = inspector.FindPrims(bigSpherePredicate, 1); ASSERT_EQ(foundPrims.size(), 1u);//The big sphere should be visible, as the filtering is disabled since the cube which is its parent node is hidden. //Unhide the cube shape node visibilityPlug.setBool(true); - + + // Refresh to update the filtering scene index chain + M3dView::active3dView().refresh(false, true); + foundPrims = inspector.FindPrims(bigSpherePredicate, 1); ASSERT_EQ(foundPrims.size(), 0u);//The big sphere should not be visible, as filtering is applied again diff --git a/test/lib/mayaUsd/render/mayaToHydra/testFlowPluginsHierarchicalProperties.py b/test/lib/mayaUsd/render/mayaToHydra/testFlowPluginsHierarchicalProperties.py new file mode 100644 index 0000000000..a8cfcb8b6d --- /dev/null +++ b/test/lib/mayaUsd/render/mayaToHydra/testFlowPluginsHierarchicalProperties.py @@ -0,0 +1,201 @@ +# +# Copyright 2024 Autodesk, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import maya.cmds as cmds +import fixturesUtils +import mtohUtils +import testUtils +import usdUtils + +class TestFlowPluginsHierarchicalProperties(mtohUtils.MayaHydraBaseTestCase): + # MayaHydraBaseTestCase.setUpClass requirement. + _file = __file__ + _requiredPlugins = ['mayaHydraFlowViewportAPILocator'] + + IMAGE_DIFF_FAIL_THRESHOLD = 0.05 + IMAGE_DIFF_FAIL_PERCENT = 1 + + def keyframeAttribute(self, object, attr, value): + cmds.setAttr(object + "." + attr, value) + cmds.setKeyframe(object, attribute=attr) + + def locatorSetup(self): + # Sphere that should be filtered by the locator filtering scene index (>10k vertices) + sphereNode = cmds.polySphere()[1] + cmds.move(0, 0, 5) + cmds.setAttr(sphereNode + ".subdivisionsAxis", 200) + cmds.setAttr(sphereNode + ".subdivisionsHeight", 200) + cmds.select(clear=True) + + locatorGrandParent = cmds.group(empty=True) + locatorParent = cmds.group(empty=True, parent=locatorGrandParent) + return locatorGrandParent, locatorParent + + def usdStageSetup(self): + # Set up a USD stage a with a cube + import mayaUsd_createStageWithNewLayer + import mayaUsd.lib + from pxr import UsdGeom + stagePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(stagePath).GetStage() + UsdGeom.Cube.Define(stage, "/Cube") + cmds.select(clear=True) + stageParent = cmds.group(empty=True) + cmds.parent(stagePath, stageParent) + stageShape = stagePath.split('|')[-1] + return stageParent, stageShape + + def usdStageAnimatedPrimSetup(self): + usdScenePath = testUtils.getTestScene('testFlowPluginsHierarchicalProperties', 'usd_animated_prim.usda') + stagePath = usdUtils.createStageFromFile(usdScenePath) + stageParent = cmds.group(empty=True) + cmds.parent(stagePath, stageParent) + stageTransform = stagePath.split('|')[1] + return stageParent, stageTransform + + def test_Authoring_Locator(self): + self.setBasicCam(10) + + locatorGrandParent, locatorParent = self.locatorSetup() + + # Ensure the initial visibility is set correctly if a parent's visibility affects it + cmds.setAttr(locatorGrandParent + ".visibility", False) + locatorShape = cmds.createNode("MhFlowViewportAPILocator", parent=locatorParent) + self.assertSnapshotClose("authoring_locator_visibility_off.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + # Check that updating a parent's visibility works after creation + cmds.setAttr(locatorGrandParent + ".visibility", True) + self.assertSnapshotClose("authoring_locator_visibility_on.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + # Check that changing the visibility on the shape itself works + cmds.setAttr(locatorShape + ".visibility", False) + self.assertSnapshotClose("authoring_locator_visibility_off.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + # Restore visibility + cmds.setAttr(locatorShape + ".visibility", True) + self.assertSnapshotClose("authoring_locator_visibility_on.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + # Change a parent transform + cmds.xform(locatorGrandParent, translation=[1,-2,3], rotation=[5,-10,15], scale=[1,-2,3]) + self.assertSnapshotClose("authoring_locator_parentTransformChanged.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + # Change the shape's transform directly + cmds.xform(cmds.listRelatives(locatorShape, parent=True)[0], translation=[-3,2,-1], rotation=[-15,10,-5], scale=[-2.5, 2.0, -1.5]) + self.assertSnapshotClose("authoring_locator_shapeTransformChanged.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + def test_Authoring_UsdStage(self): + self.setBasicCam(10) + + stageParent, stageShape = self.usdStageSetup() + + # Hide/unhide parent + self.assertSnapshotAndCompareVp2("usdStage_visibility_on.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + cmds.setAttr(stageParent + ".visibility", False) + self.assertSnapshotAndCompareVp2("usdStage_visibility_off.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + cmds.setAttr(stageParent + ".visibility", True) + self.assertSnapshotAndCompareVp2("usdStage_visibility_on.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + # Hide/unhide the shape directly + cmds.setAttr(stageShape + ".visibility", False) + self.assertSnapshotAndCompareVp2("usdStage_visibility_off.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + cmds.setAttr(stageShape + ".visibility", True) + self.assertSnapshotAndCompareVp2("usdStage_visibility_on.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + # Change a parent transform + cmds.xform(stageParent, translation=[1,-2,3], rotation=[5,-10,15], scale=[1,-2,3]) + self.assertSnapshotAndCompareVp2("usdStage_parentTransformChanged.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + # Change the shape's transform directly + cmds.xform(cmds.listRelatives(stageShape, parent=True)[0], translation=[-3,2,-1], rotation=[-15,10,-5], scale=[-2.5, 2.0, -1.5]) + self.assertSnapshotAndCompareVp2("usdStage_shapeTransformChanged.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + def test_Playback_Locator(self): + self.setBasicCam(10) + + locatorGrandParent, locatorParent = self.locatorSetup() + cmds.createNode("MhFlowViewportAPILocator", parent=locatorParent) + + cmds.currentTime(0) + self.keyframeAttribute(locatorGrandParent, "visibility", True) + self.keyframeAttribute(locatorGrandParent, "translateX", 0) + + cmds.currentTime(5) + self.keyframeAttribute(locatorGrandParent, "visibility", False) + self.keyframeAttribute(locatorGrandParent, "translateX", 15) + + cmds.currentTime(0) + self.assertSnapshotClose("locator_playback_initial.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + cmds.currentTime(2) + self.assertSnapshotClose("locator_playback_translated.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + cmds.currentTime(7) + self.assertSnapshotClose("locator_playback_hidden.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + def test_Playback_UsdStage(self): + self.setBasicCam(10) + + stageParent, _ = self.usdStageSetup() + + cmds.currentTime(0) + self.keyframeAttribute(stageParent, "visibility", True) + self.keyframeAttribute(stageParent, "translateX", 0) + + cmds.currentTime(5) + self.keyframeAttribute(stageParent, "visibility", False) + self.keyframeAttribute(stageParent, "translateX", 15) + + cmds.currentTime(0) + self.assertSnapshotClose("usdStage_playback_initial.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + cmds.currentTime(2) + self.assertSnapshotClose("usdStage_playback_translated.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + cmds.currentTime(7) + self.assertSnapshotClose("usdStage_playback_hidden.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + self.setViewport2Renderer() + + cmds.currentTime(0) + self.assertSnapshotSilhouetteClose("usdStage_playback_initial.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + cmds.currentTime(2) + self.assertSnapshotSilhouetteClose("usdStage_playback_translated.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + cmds.currentTime(7) + self.assertSnapshotSilhouetteClose("usdStage_playback_hidden.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + def test_UsdStageAnimatedPrim(self): + self.setBasicCam(10) + + stageParent, stageTransform = self.usdStageAnimatedPrimSetup() + + cmds.currentTime(0) + self.keyframeAttribute(stageParent, "translateY", 0) + self.keyframeAttribute(stageTransform, "translateZ", 0) + + cmds.currentTime(4) + self.keyframeAttribute(stageParent, "translateY", 5) + self.keyframeAttribute(stageTransform, "translateZ", 5) + + # Note : frame 15 is empty, as the prim is hidden + checkedTimes = [0, 1, 3, 5, 7, 10, 12, 15] + + for time in checkedTimes: + cmds.currentTime(time) + self.assertSnapshotClose("usdStageAnimatedPrim_t" + str(time) + ".png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + + self.setViewport2Renderer() + for time in checkedTimes: + cmds.currentTime(time) + self.assertSnapshotSilhouetteClose("usdStageAnimatedPrim_t" + str(time) + ".png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT) + +if __name__ == '__main__': + fixturesUtils.runTests(globals()) diff --git a/test/testSamples/testFlowPluginsHierarchicalProperties/usd_animated_prim.usda b/test/testSamples/testFlowPluginsHierarchicalProperties/usd_animated_prim.usda new file mode 100644 index 0000000000..9e0f23acb5 --- /dev/null +++ b/test/testSamples/testFlowPluginsHierarchicalProperties/usd_animated_prim.usda @@ -0,0 +1,69 @@ +#usda 1.0 + +def Mesh "pCylinder1" ( + kind = "component" +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-1.0000002, -1, -1.0000005), (1, 1, 1.0000001)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + int[] faceVertexIndices = [0, 1, 21, 20, 1, 2, 22, 21, 2, 3, 23, 22, 3, 4, 24, 23, 4, 5, 25, 24, 5, 6, 26, 25, 6, 7, 27, 26, 7, 8, 28, 27, 8, 9, 29, 28, 9, 10, 30, 29, 10, 11, 31, 30, 11, 12, 32, 31, 12, 13, 33, 32, 13, 14, 34, 33, 14, 15, 35, 34, 15, 16, 36, 35, 16, 17, 37, 36, 17, 18, 38, 37, 18, 19, 39, 38, 19, 0, 20, 39, 1, 0, 40, 2, 1, 40, 3, 2, 40, 4, 3, 40, 5, 4, 40, 6, 5, 40, 7, 6, 40, 8, 7, 40, 9, 8, 40, 10, 9, 40, 11, 10, 40, 12, 11, 40, 13, 12, 40, 14, 13, 40, 15, 14, 40, 16, 15, 40, 17, 16, 40, 18, 17, 40, 19, 18, 40, 0, 19, 40, 20, 21, 41, 21, 22, 41, 22, 23, 41, 23, 24, 41, 24, 25, 41, 25, 26, 41, 26, 27, 41, 27, 28, 41, 28, 29, 41, 29, 30, 41, 30, 31, 41, 31, 32, 41, 32, 33, 41, 33, 34, 41, 34, 35, 41, 35, 36, 41, 36, 37, 41, 37, 38, 41, 38, 39, 41, 39, 20, 41] + point3f[] points = [(0.95105714, -1, -0.30901718), (0.80901754, -1, -0.5877856), (0.5877856, -1, -0.8090175), (0.30901715, -1, -0.951057), (0, -1, -1.0000005), (-0.30901715, -1, -0.95105696), (-0.5877855, -1, -0.8090173), (-0.80901724, -1, -0.5877854), (-0.9510568, -1, -0.30901706), (-1.0000002, -1, 0), (-0.9510568, -1, 0.30901706), (-0.8090172, -1, 0.58778536), (-0.58778536, -1, 0.8090171), (-0.30901706, -1, 0.95105666), (-2.9802322e-8, -1, 1.0000001), (0.30901697, -1, 0.9510566), (0.58778524, -1, 0.80901706), (0.809017, -1, 0.5877853), (0.95105654, -1, 0.309017), (1, -1, 0), (0.95105714, 1, -0.30901718), (0.80901754, 1, -0.5877856), (0.5877856, 1, -0.8090175), (0.30901715, 1, -0.951057), (0, 1, -1.0000005), (-0.30901715, 1, -0.95105696), (-0.5877855, 1, -0.8090173), (-0.80901724, 1, -0.5877854), (-0.9510568, 1, -0.30901706), (-1.0000002, 1, 0), (-0.9510568, 1, 0.30901706), (-0.8090172, 1, 0.58778536), (-0.58778536, 1, 0.8090171), (-0.30901706, 1, 0.95105666), (-2.9802322e-8, 1, 1.0000001), (0.30901697, 1, 0.9510566), (0.58778524, 1, 0.80901706), (0.809017, 1, 0.5877853), (0.95105654, 1, 0.309017), (1, 1, 0), (0, -1, 0), (0, 1, 0)] + color3f[] primvars:displayColor = [(0.13320851, 0.13320851, 0.13320851)] ( + customData = { + dictionary Maya = { + bool generated = 1 + } + } + ) + texCoord2f[] primvars:st = [(0.64860266, 0.107966065), (0.626409, 0.064408496), (0.5918415, 0.02984102), (0.54828393, 0.0076473355), (0.5, -7.4505806e-8), (0.45171607, 0.0076473504), (0.4081585, 0.02984105), (0.37359107, 0.064408526), (0.3513974, 0.107966095), (0.34374997, 0.15625), (0.3513974, 0.2045339), (0.37359107, 0.24809146), (0.40815854, 0.28265893), (0.4517161, 0.3048526), (0.5, 0.3125), (0.5482839, 0.3048526), (0.59184146, 0.28265893), (0.62640893, 0.24809146), (0.6486026, 0.2045339), (0.65625, 0.15625), (0.375, 0.3125), (0.3875, 0.3125), (0.39999998, 0.3125), (0.41249996, 0.3125), (0.42499995, 0.3125), (0.43749994, 0.3125), (0.44999993, 0.3125), (0.46249992, 0.3125), (0.4749999, 0.3125), (0.4874999, 0.3125), (0.49999988, 0.3125), (0.51249987, 0.3125), (0.52499986, 0.3125), (0.53749985, 0.3125), (0.54999983, 0.3125), (0.5624998, 0.3125), (0.5749998, 0.3125), (0.5874998, 0.3125), (0.5999998, 0.3125), (0.6124998, 0.3125), (0.62499976, 0.3125), (0.375, 0.6875), (0.3875, 0.6875), (0.39999998, 0.6875), (0.41249996, 0.6875), (0.42499995, 0.6875), (0.43749994, 0.6875), (0.44999993, 0.6875), (0.46249992, 0.6875), (0.4749999, 0.6875), (0.4874999, 0.6875), (0.49999988, 0.6875), (0.51249987, 0.6875), (0.52499986, 0.6875), (0.53749985, 0.6875), (0.54999983, 0.6875), (0.5624998, 0.6875), (0.5749998, 0.6875), (0.5874998, 0.6875), (0.5999998, 0.6875), (0.6124998, 0.6875), (0.62499976, 0.6875), (0.64860266, 0.79546607), (0.626409, 0.7519085), (0.5918415, 0.717341), (0.54828393, 0.69514734), (0.5, 0.68749994), (0.45171607, 0.69514734), (0.4081585, 0.71734107), (0.37359107, 0.75190854), (0.3513974, 0.79546607), (0.34374997, 0.84375), (0.3513974, 0.89203393), (0.37359107, 0.93559146), (0.40815854, 0.97015893), (0.4517161, 0.9923526), (0.5, 1), (0.5482839, 0.9923526), (0.59184146, 0.97015893), (0.62640893, 0.93559146), (0.6486026, 0.89203393), (0.65625, 0.84375), (0.5, 0.15625), (0.5, 0.84375)] ( + customData = { + dictionary Maya = { + token name = "map1" + } + } + interpolation = "faceVarying" + ) + int[] primvars:st:indices = [20, 21, 42, 41, 21, 22, 43, 42, 22, 23, 44, 43, 23, 24, 45, 44, 24, 25, 46, 45, 25, 26, 47, 46, 26, 27, 48, 47, 27, 28, 49, 48, 28, 29, 50, 49, 29, 30, 51, 50, 30, 31, 52, 51, 31, 32, 53, 52, 32, 33, 54, 53, 33, 34, 55, 54, 34, 35, 56, 55, 35, 36, 57, 56, 36, 37, 58, 57, 37, 38, 59, 58, 38, 39, 60, 59, 39, 40, 61, 60, 1, 0, 82, 2, 1, 82, 3, 2, 82, 4, 3, 82, 5, 4, 82, 6, 5, 82, 7, 6, 82, 8, 7, 82, 9, 8, 82, 10, 9, 82, 11, 10, 82, 12, 11, 82, 13, 12, 82, 14, 13, 82, 15, 14, 82, 16, 15, 82, 17, 16, 82, 18, 17, 82, 19, 18, 82, 0, 19, 82, 80, 79, 83, 79, 78, 83, 78, 77, 83, 77, 76, 83, 76, 75, 83, 75, 74, 83, 74, 73, 83, 73, 72, 83, 72, 71, 83, 71, 70, 83, 70, 69, 83, 69, 68, 83, 68, 67, 83, 67, 66, 83, 66, 65, 83, 65, 64, 83, 64, 63, 83, 63, 62, 83, 62, 81, 83, 81, 80, 83] + token visibility = "inherited" + token visibility.timeSamples = { + 14: "inherited", + 15: "invisible", + } + double3 xformOp:translate.timeSamples = { + 0: (0, 0, 0), + 1: (0, 0, 0), + 2: (0.1714677640603566, 0, 0), + 3: (0.6310013717421126, 0, 0), + 4: (1.296296296296296, 0, 0), + 5: (2.0850480109739373, 0, 0), + 6: (2.9149519890260627, 0, 0), + 7: (3.703703703703703, 0, 0), + 8: (4.368998628257886, 0, 0), + 9: (4.828532235939641, 0, 0), + 10: (5, 0, 0), + } + uniform token[] xformOpOrder = ["xformOp:translate"] + + def GeomSubset "bottom" + { + uniform token elementType = "face" + uniform token familyName = "componentTag" + int[] indices = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39] + } + + def GeomSubset "sides" + { + uniform token elementType = "face" + uniform token familyName = "componentTag" + int[] indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + } + + def GeomSubset "top" + { + uniform token elementType = "face" + uniform token familyName = "componentTag" + int[] indices = [40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59] + } +} + diff --git a/test/testUtils/imageUtils.py b/test/testUtils/imageUtils.py index 19459aff05..fabfdbf5f0 100644 --- a/test/testUtils/imageUtils.py +++ b/test/testUtils/imageUtils.py @@ -15,6 +15,7 @@ # import os import maya.cmds as cmds +import shutil import subprocess KNOWN_FORMATS = { @@ -31,6 +32,9 @@ } def snapshot(outputPath, width=400, height=None): + #Disable undo so that when we call undo it doesn't undo any operation from self.assertSnapshotClose + cmds.undoInfo(stateWithoutFlush=False) + if height is None: height = width @@ -57,6 +61,9 @@ def snapshot(outputPath, width=400, height=None): finally: cmds.setAttr("defaultRenderGlobals.imageFormat", oldFormat) + #Enable undo again + cmds.undoInfo(stateWithoutFlush=True) + def imageDiff(imagePath1, imagePath2, verbose, fail, failpercent, hardfail, warn, warnpercent, hardwarn, perceptual): """ Returns the completed process instance after running idiff. @@ -115,9 +122,38 @@ def imageDiff(imagePath1, imagePath2, verbose, fail, failpercent, hardfail, proc = subprocess.run(cmd, shell=False, env=os.environ.copy(), stdout=subprocess.PIPE) return proc +def convertToSilhouette(imagePath): + # 2024-06-13 : Tried to use oiiotool instead of PySide for this to be more efficient, + # however it did not work under certain circumstances, for example when trying to + # run it on the copied reference image assertSnapshotSilhouetteClose. It did work on + # captured snapshots for some reason. The error oiiotool gave out was something like + # "Could not open file someFileName.[randomAlphaNumericCharacters].temp.[png|jpg]". + # The temp file mentioned is itself being created by OIIO. Attaching a debugger is not + # straightforward at least on Windows, as the process is started by the tests, and + # trying to run oiiotool independently did not work due to not finding boost libs. + # Decided not to investigate further for now, as this is an implementation detail + # that can always be swapped out later if we need the performance, and it is not + # going to be used in most cases. + from PySide6.QtGui import QImage, QColor + + image = QImage(imagePath) + + for x in range(image.width()): + for y in range(image.height()): + if image.pixelColor(x, y).alpha() > 0: + image.setPixelColor(x, y, QColor(255, 255, 255, 255)) + + image.save(imagePath) + class ImageDiffingTestCase: '''Mixin class for unit tests that require image comparison.''' + def getSnapshotDir(self): + snapshotDir = os.path.join(os.path.abspath('.'), self._testMethodName) + if not os.path.isdir(snapshotDir): + os.makedirs(snapshotDir) + return snapshotDir + def assertImagesClose(self, imagePath1, imagePath2, fail, failpercent, hardfail=None, warn=None, warnpercent=None, hardwarn=None, perceptual=False): """ @@ -146,23 +182,33 @@ def assertImagesClose(self, imagePath1, imagePath2, fail, failpercent, hardfail= def assertImagesEqual(self, imagePath1, imagePath2): self.assertImagesClose(imagePath1, imagePath2, fail=None, failpercent=None) - def assertSnapshotClose(self, refImage, fail, failpercent, hardfail=None, + def assertSnapshotClose(self, refImagePath, fail, failpercent, hardfail=None, warn=None, warnpercent=None, hardwarn=None, perceptual=False): - #Disable undo so that when we call undo it doesn't undo any operation from self.assertSnapshotClose - cmds.undoInfo(stateWithoutFlush=False) - snapDir = os.path.join(os.path.abspath('.'), self._testMethodName) - if not os.path.isdir(snapDir): - os.makedirs(snapDir) - snapImage = os.path.join(snapDir, os.path.basename(refImage)) - snapshot(snapImage) - #Enable undo again - cmds.undoInfo(stateWithoutFlush=True) + snapImagePath = os.path.join(self.getSnapshotDir(), os.path.basename(refImagePath)) + snapshot(snapImagePath) - return self.assertImagesClose(refImage, snapImage, + return self.assertImagesClose(refImagePath, snapImagePath, fail=fail, failpercent=failpercent, hardfail=hardfail, warn=warn, warnpercent=warnpercent, hardwarn=hardwarn, perceptual=perceptual) - def assertSnapshotEqual(self, refImage): + def assertSnapshotEqual(self, refImagePath): '''Use of this method is discouraged, as renders can vary slightly between renderer architectures.''' - return self.assertSnapshotClose(refImage, fail=None, failpercent=None) + return self.assertSnapshotClose(refImagePath, fail=None, failpercent=None) + + def assertSnapshotSilhouetteClose(self, refImagePath, fail, failpercent, hardfail=None, + warn=None, warnpercent=None, hardwarn=None, perceptual=False): + refImageName, refImageExtension = os.path.splitext(os.path.basename(refImagePath)) + + refSilhouetteImagePath = os.path.join(self.getSnapshotDir(), refImageName + "_ReferenceSilhouette" + refImageExtension) + shutil.copy(refImagePath, refSilhouetteImagePath) + convertToSilhouette(refSilhouetteImagePath) + + snapSilhouetteImagePath = os.path.join(self.getSnapshotDir(), refImageName + "_SnapshotSilhouette" + refImageExtension) + snapshot(snapSilhouetteImagePath) + convertToSilhouette(snapSilhouetteImagePath) + + return self.assertImagesClose(refSilhouetteImagePath, snapSilhouetteImagePath, + fail=fail, failpercent=failpercent, hardfail=hardfail, + warn=warn, warnpercent=warnpercent, hardwarn=hardwarn, + perceptual=perceptual) diff --git a/test/testUtils/mtohUtils.py b/test/testUtils/mtohUtils.py index d45f4b8a65..79d19d630e 100644 --- a/test/testUtils/mtohUtils.py +++ b/test/testUtils/mtohUtils.py @@ -197,14 +197,30 @@ def assertImagesEqual(self, image1, image2, image1Version=None, image2Version=No def assertSnapshotClose(self, refImage, fail, failpercent, imageVersion=None, hardfail=None, warn=None, warnpercent=None, hardwarn=None, perceptual=False): - refImage = self.resolveRefImage(refImage, imageVersion) - super(MayaHydraBaseTestCase, self).assertSnapshotClose(refImage, fail, failpercent, hardfail, + refImagePath = self.resolveRefImage(refImage, imageVersion) + super(MayaHydraBaseTestCase, self).assertSnapshotClose(refImagePath, fail, failpercent, hardfail, warn, warnpercent, hardwarn, perceptual) def assertSnapshotEqual(self, refImage, imageVersion=None): '''Use of this method is discouraged, as renders can vary slightly between renderer architectures.''' - refImage = self.resolveRefImage(refImage, imageVersion) - super(MayaHydraBaseTestCase, self).assertSnapshotEqual(refImage) + refImagePath = self.resolveRefImage(refImage, imageVersion) + super(MayaHydraBaseTestCase, self).assertSnapshotEqual(refImagePath) + + def assertSnapshotSilhouetteClose(self, refImage, fail, failpercent, imageVersion=None, hardfail=None, + warn=None, warnpercent=None, hardwarn=None, perceptual=False): + refImagePath = self.resolveRefImage(refImage, imageVersion) + super(MayaHydraBaseTestCase, self).assertSnapshotSilhouetteClose(refImagePath, fail, failpercent, hardfail, + warn, warnpercent, hardwarn, perceptual) + + def assertSnapshotAndCompareVp2(self, refImage, fail, failpercent, imageVersion=None, hardfail=None, + warn=None, warnpercent=None, hardwarn=None, perceptual=False): + self.setHdStormRenderer() + self.assertSnapshotClose(refImage, fail, failpercent, imageVersion, hardfail, warn, warnpercent, hardwarn, perceptual) + + self.setViewport2Renderer() + self.assertSnapshotSilhouetteClose(refImage, fail, failpercent, imageVersion, hardfail, warn, warnpercent, hardwarn, perceptual) + + self.setHdStormRenderer() def runCppTest(self, testFilter): with PluginLoaded("mayaHydraCppTests"):