From 78129d15ae4862c93301f8ee59114d0f8a3b4f3a Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 21 Dec 2023 16:49:56 -0500 Subject: [PATCH 1/5] Made proxy shape scene index prefixes unique, and support rename / reparent. --- lib/mayaHydra/hydraExtensions/hydraUtils.cpp | 70 ++++ lib/mayaHydra/hydraExtensions/hydraUtils.h | 35 ++ .../sceneIndex/registration.cpp | 305 ++++++++++-------- .../hydraExtensions/sceneIndex/registration.h | 2 - .../mayaUsd/render/mayaToHydra/CMakeLists.txt | 1 + .../render/mayaToHydra/cpp/CMakeLists.txt | 1 + .../mayaToHydra/cpp/mayaHydraCppTestsCmd.cpp | 52 +-- .../mayaToHydra/cpp/testPathInterface.cpp | 87 +++++ .../mayaToHydra/cpp/testPathInterface.py | 123 +++++++ .../render/mayaToHydra/cpp/testUtils.cpp | 18 ++ .../render/mayaToHydra/cpp/testUtils.h | 25 ++ 11 files changed, 565 insertions(+), 154 deletions(-) create mode 100644 test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp create mode 100644 test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py diff --git a/lib/mayaHydra/hydraExtensions/hydraUtils.cpp b/lib/mayaHydra/hydraExtensions/hydraUtils.cpp index 62b1986c99..1587e4a222 100644 --- a/lib/mayaHydra/hydraExtensions/hydraUtils.cpp +++ b/lib/mayaHydra/hydraExtensions/hydraUtils.cpp @@ -24,6 +24,8 @@ #include #include +#include + PXR_NAMESPACE_USING_DIRECTIVE // This is the delimiter that Maya uses to identify levels of hierarchy in the @@ -225,6 +227,16 @@ void SanitizeNameForSdfPath(std::string& inoutPathString, bool doStripNamespaces std::replace(inoutPathString.begin(), inoutPathString.end(), ';', '_'); } +std::string SanitizeNameForSdfPath( + const std::string& pathString, + bool doStripNamespaces /* = false */ +) +{ + auto rhs = pathString; + SanitizeNameForSdfPath(rhs, doStripNamespaces); + return rhs; +} + SdfPath MakeRelativeToParentPath(const SdfPath& path) { return path.MakeRelativePath(path.GetParentPath()); @@ -253,5 +265,63 @@ void GetDirectionalLightPositionFromDirectionVector(GfVec3f& outPosition, const outPosition = {-farfarAway*direction.data()[0], -farfarAway*direction.data()[1], -farfarAway*direction.data()[2]}; } +bool splitNumericalSuffix(const std::string& srcName, std::string& base, std::string& suffix) +{ + // Compiled regular expression to find a numerical suffix to a path component. + // It searches for any number of characters followed by a single non-numeric, + // then one or more digits at end of string. + const static std::regex re("(.*)([^0-9])([0-9]+)$"); + base = srcName; + std::smatch match; + if (std::regex_match(srcName, match, re)) { + base = match[1].str() + match[2].str(); + suffix = match[3].str(); + return true; + } + return false; +} + +std::string uniqueName(const TfToken::HashSet& existingNames, std::string srcName) +{ + if (existingNames.empty() || (existingNames.count(TfToken(srcName)) == 0u)) { + return srcName; + } + + std::string base, suffixStr; + int suffix { 1 }; + size_t lenSuffix { 1 }; + if (splitNumericalSuffix(srcName, base, suffixStr)) { + lenSuffix = suffixStr.length(); + suffix = std::stoi(suffixStr) + 1; + } + + // Create a suffix string from the number keeping the same number of digits + // as numerical suffix from input srcName (padding with 0's if needed). + suffixStr = std::to_string(suffix); + suffixStr = std::string(lenSuffix - std::min(lenSuffix, suffixStr.length()), '0') + suffixStr; + std::string dstName = base + suffixStr; + while (existingNames.count(TfToken(dstName)) > 0) { + suffixStr = std::to_string(++suffix); + suffixStr + = std::string(lenSuffix - std::min(lenSuffix, suffixStr.length()), '0') + suffixStr; + dstName = base + suffixStr; + } + return dstName; +} + +PXR_NS::TfToken uniqueChildName( + const PXR_NS::HdSceneIndexBaseRefPtr& sceneIndex, + const PXR_NS::SdfPath& parent, + const std::string& childName +) +{ + auto childPrims = sceneIndex->GetChildPrimPaths(parent); + TfToken::HashSet existingNames; + for (const auto& child : childPrims) { + existingNames.insert(child.GetNameToken()); + } + + return TfToken(uniqueName(existingNames, childName)); +} } // namespace MAYAHYDRA_NS_DEF diff --git a/lib/mayaHydra/hydraExtensions/hydraUtils.h b/lib/mayaHydra/hydraExtensions/hydraUtils.h index 7655de997e..294ee7be30 100644 --- a/lib/mayaHydra/hydraExtensions/hydraUtils.h +++ b/lib/mayaHydra/hydraExtensions/hydraUtils.h @@ -64,6 +64,17 @@ std::string StripNamespaces(const std::string& nodeName, const int nsDepth = -1) MAYAHYDRALIB_API void SanitizeNameForSdfPath(std::string& inOutPathString, bool doStripNamespaces = false); +/** + * @brief Replaces the invalid characters for SdfPath in \p pathString. + * + * @param[in] pathString is the path string to sanitize. + * @param[in] doStripNamespaces determines whether to strip namespaces or not. + * + * @return The sanitized version of \p pathString. + */ +MAYAHYDRALIB_API +std::string SanitizeNameForSdfPath(const std::string& pathString, bool doStripNamespaces = false); + /** * @brief Get the given SdfPath without its parent path. * @@ -103,6 +114,30 @@ bool GetXformMatrixFromPrim(const PXR_NS::HdSceneIndexPrim& prim, PXR_NS::GfMatr MAYAHYDRALIB_API void GetDirectionalLightPositionFromDirectionVector(PXR_NS::GfVec3f& outPosition, const PXR_NS::GfVec3f& direction); +//! Split the input source name

into a base name

and a +//! numerical suffix (if present)

. +//! Returns true when numerical suffix was found, otherwise false. +MAYAHYDRALIB_API +bool splitNumericalSuffix(const std::string& srcName, std::string& base, std::string& suffix); + +//! Return a name based on

that is not in the set of

. If srcName is not in existingNames, it is returned +//! unchanged. If it is in existingNames, try to extract a numerical suffix +//! from srcName (set to 1 if none). The resulting name is checked against +//! existingNames, with the suffix incremented until the resulting name is +//! unique. +MAYAHYDRALIB_API +std::string uniqueName(const PXR_NS::TfToken::HashSet& existingNames, std::string srcName); + +//! Use uniqueName() to return a name based on

that is not in the +//! existing children of

. +MAYAHYDRALIB_API +PXR_NS::TfToken uniqueChildName( + const PXR_NS::HdSceneIndexBaseRefPtr& sceneIndex, + const PXR_NS::SdfPath& parent, + const std::string& childName +); + } // namespace MAYAHYDRA_NS_DEF #endif // MAYAHYDRALIB_HYDRA_UTILS_H diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp index c80c28ab09..7c4285cb41 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp @@ -28,19 +28,6 @@ #include #include -// The SceneIndex plugin required for MaterialX binding to work will no longer be needed -// after 23.08 hopefully. Pixar has made changed to USD that should handle this case. -#if PXR_VERSION < 2308 -// Temp workaround to get the code to compile. -// this will not be required once the pull request to USD header is complete. -// Details of the issue - https://groups.google.com/g/usd-interest/c/0kyY3Z2sgjo -// TODO: remove conditional #undefs after PR is complete. -#ifdef interface -#undef interface -#endif -#include -#endif //PXR_VERSION - #include #include #include @@ -49,10 +36,40 @@ #include +#include +#include +#include + namespace { // Pixar macros won't work without PXR_NS. PXR_NAMESPACE_USING_DIRECTIVE +using namespace Ufe; + +// UFE Observer that unpacks SceneCompositeNotification's. Belongs in UFE +// itself. +class SceneObserver : public Observer +{ +public: + SceneObserver() = default; + + virtual void handleOp(const SceneCompositeNotification::Op& op) = 0; + +private: + void operator()(const Notification& notification) override + { + const auto& sceneChanged = notification.staticCast(); + + if (SceneChanged::SceneCompositeNotification == sceneChanged.opType()) { + const auto& compNotification = notification.staticCast(); + for(const auto& op : compNotification) { + handleOp(op); + } + } else { + handleOp(sceneChanged); + } + } +}; /// \class PathInterfaceSceneIndex /// @@ -67,11 +84,12 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase static HdSceneIndexBaseRefPtr New( const HdSceneIndexBaseRefPtr& inputSceneIndex, - const SdfPath& sceneIndexPathPrefix + const SdfPath& sceneIndexPathPrefix, + const Ufe::Path& sceneIndexAppPath ) { - return TfCreateRefPtr( - new PathInterfaceSceneIndex(inputSceneIndex, sceneIndexPathPrefix)); + return TfCreateRefPtr(new PathInterfaceSceneIndex( + inputSceneIndex, sceneIndexPathPrefix, sceneIndexAppPath)); } SdfPath SceneIndexPath(const Ufe::Path& appPath) const override @@ -82,14 +100,9 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase return {}; } - // Determine if the application path maps to our scene index. At time - // of writing the mapping is that the final component of the scene - // index prefix is the Maya USD proxy shape node name, which is the - // final component of the Ufe::Path first segment. This is weak and - // non-unique, and incrementing integer suffix schemes to fix this are - // unintuitive and difficult to understand. PPT, 26-Oct-2023. - if (appPath.getSegments()[0].components().back().string() != - _sceneIndexPathPrefix.GetName()) { + // If the data model object application path does not match the path we + // translate, return an empty path. + if (!appPath.startsWith(_sceneIndexAppPath)) { return {}; } @@ -107,16 +120,65 @@ class PathInterfaceSceneIndex : public Fvp::PathInterfaceSceneIndexBase return sceneIndexPath; } + const Ufe::Path& GetSceneIndexAppPath() const { return _sceneIndexAppPath; } + void SetSceneIndexAppPath(const Ufe::Path& sceneIndexAppPath) { + _sceneIndexAppPath = sceneIndexAppPath; + } + private: + class PathInterfaceSceneObserver : public SceneObserver + { + public: + PathInterfaceSceneObserver(PathInterfaceSceneIndex& pi) + : SceneObserver(), _pi(pi) + {} + + private: + + void handleOp(const SceneCompositeNotification::Op& op) override { + if (op.opType == SceneChanged::ObjectPathChange && + ((op.subOpType == ObjectPathChange::ObjectReparent) || + (op.subOpType == ObjectPathChange::ObjectRename))) { + const auto& siPath = _pi.GetSceneIndexAppPath(); + if (siPath.startsWith(op.path)) { + _pi.SetSceneIndexAppPath(siPath.reparent(op.path, op.item->path())); + } + } + } + + PathInterfaceSceneIndex& _pi; + }; + PathInterfaceSceneIndex( const HdSceneIndexBaseRefPtr& inputSceneIndex, - const SdfPath& sceneIndexPathPrefix + const SdfPath& sceneIndexPathPrefix, + const Ufe::Path& sceneIndexAppPath + ) : PathInterfaceSceneIndexBase(inputSceneIndex) , _sceneIndexPathPrefix(sceneIndexPathPrefix) - {} + , _appSceneObserver(std::make_shared(*this)) + , _sceneIndexAppPath(sceneIndexAppPath) + { + // The gateway node (proxy shape) is a Maya node, so the scene index + // path must be a single segment. + TF_AXIOM(sceneIndexAppPath.nbSegments() == 1); + + // Observe the scene to be informed of path changes to the gateway node + // (proxy shape) that corresponds to our scene index data producer. + Scene::instance().addObserver(_appSceneObserver); + } - SdfPath _sceneIndexPathPrefix; + ~PathInterfaceSceneIndex() { + // Ufe::Subject has automatic cleanup of stale observers, but this can + // be problematic on application exit if the library of the observer is + // cleaned up before that of the subject, so simply stop observing. + Scene::instance().removeObserver(_appSceneObserver); + } + + const SdfPath _sceneIndexPathPrefix; + const Observer::Ptr _appSceneObserver; + Ufe::Path _sceneIndexAppPath; }; } @@ -265,23 +327,6 @@ bool MayaHydraSceneIndexRegistry::_RemoveSceneIndexForNode(const MObject& dagNod return false; } -#if PXR_VERSION < 2308 -HdSceneIndexBaseRefPtr -MayaHydraSceneIndexRegistry::_AppendTerminalRenamingSceneIndex(const HdSceneIndexBaseRefPtr& sceneIndex) -{ - // Get the list of renderer supported material network implementations. - TfTokenVector renderingContexts = - _renderIndexProxy.GetMaterialRenderContexts(); - - // Create remapping token pairs to help Hydra build the material networks. - std::map terminalRemapList; - for (const auto& terminal : renderingContexts) - terminalRemapList.emplace(TfToken(terminal.GetString()+":surface"), - HdMaterialTerminalTokens->surface); - return HdsiTerminalsResolvingSceneIndex::New(sceneIndex, terminalRemapList); -} -#endif // PXR_VERSION - constexpr char kSceneIndexPluginSuffix[] = { "MayaNodeSceneIndexPlugin" }; // every scene index plugin compatible with the hydra viewport requires this suffix @@ -289,100 +334,92 @@ constexpr char kSceneIndexPluginSuffix[] = { void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) { MFnDependencyNode dependNodeFn(dagNode); - // Name must match Plugin TfType registration thus must begin with upper case - std::string sceneIndexPluginName(dependNodeFn.typeName().asChar()); - sceneIndexPluginName[0] = toupper(sceneIndexPluginName[0]); - sceneIndexPluginName += kSceneIndexPluginSuffix; - TfToken sceneIndexPluginId(sceneIndexPluginName); + // To match plugin TfType registration, name must begin with upper case. + const std::string sceneIndexPluginName([&](){ + std::string name = dependNodeFn.typeName().asChar(); + name[0] = toupper(name[0]); + name += kSceneIndexPluginSuffix; + return name;}()); + const TfToken sceneIndexPluginId(sceneIndexPluginName); static HdSceneIndexPluginRegistry& sceneIndexPluginRegistry = HdSceneIndexPluginRegistry::GetInstance(); - if (sceneIndexPluginRegistry.IsRegisteredPlugin(sceneIndexPluginId)) { - using MayaHydraMObjectDataSource = HdRetainedTypedSampledDataSource; - using MayaHydraVersionDataSource = HdRetainedTypedSampledDataSource; - // Functions retrieved from the scene index plugin - using MayaHydraInterpretRprimPathDataSource - = HdRetainedTypedSampledDataSource; - - // Create the registration record which is then added into the registry if everything - // succeeds - static TfToken sDataSourceEntryNames[] { TfToken("object"), - TfToken("version"), - TfToken("interpretRprimPath") }; - constexpr int kDataSourceNumEntries = sizeof(sDataSourceEntryNames) / sizeof(TfToken); - MayaHydraSceneIndexRegistrationPtr registration(new MayaHydraSceneIndexRegistration()); - HdDataSourceBaseHandle values[] { MayaHydraMObjectDataSource::New(dagNode), - MayaHydraVersionDataSource::New(MAYAHYDRA_API_VERSION), - MayaHydraInterpretRprimPathDataSource::New( - registration->interpretRprimPathFn) }; - static_assert( - sizeof(values) / sizeof(HdDataSourceBaseHandle) == kDataSourceNumEntries, - "Incorrect number of data source entries"); - registration->pluginSceneIndex = sceneIndexPluginRegistry.AppendSceneIndex( - sceneIndexPluginId, - nullptr, - HdRetainedContainerDataSource::New(kDataSourceNumEntries, sDataSourceEntryNames, values)); - if (TF_VERIFY( + if (!sceneIndexPluginRegistry.IsRegisteredPlugin(sceneIndexPluginId)) { + return; + } + + using MObjectDataSource = HdRetainedTypedSampledDataSource; + using MayaHydraVersionDataSource = HdRetainedTypedSampledDataSource; + // Functions retrieved from the scene index plugin + using InterpretRprimPathDataSource + = HdRetainedTypedSampledDataSource; + + // Create the registration record which is then added into the registry + // if everything succeeds. + static TfToken sDataSourceEntryNames[] { + TfToken("object"), TfToken("version"), TfToken("interpretRprimPath") + }; + constexpr int kDataSourceNumEntries = sizeof(sDataSourceEntryNames) / sizeof(TfToken); + MayaHydraSceneIndexRegistrationPtr registration(new MayaHydraSceneIndexRegistration()); + HdDataSourceBaseHandle values[] { + MObjectDataSource::New(dagNode), + MayaHydraVersionDataSource::New(MAYAHYDRA_API_VERSION), + InterpretRprimPathDataSource::New(registration->interpretRprimPathFn) + }; + static_assert( + sizeof(values) / sizeof(HdDataSourceBaseHandle) == kDataSourceNumEntries, + "Incorrect number of data source entries"); + registration->pluginSceneIndex = sceneIndexPluginRegistry.AppendSceneIndex( + sceneIndexPluginId, + nullptr, + HdRetainedContainerDataSource::New(kDataSourceNumEntries, sDataSourceEntryNames, values)); + if (TF_VERIFY( + registration->pluginSceneIndex, + "HdSceneIndexBase::AppendSceneIndex failed to create %s scene index from given " + "node type.", + sceneIndexPluginName.c_str())) { + + MStatus status; + MDagPath dagPath(MDagPath::getAPathTo(dagNode, &status)); + if (TF_VERIFY(status == MS::kSuccess, "Unable of finding Dag path to given node")) { + registration->dagNode = MObjectHandle(dagNode); + + // Create a unique scene index path prefix by starting with the + // Dag node name, and checking for uniqueness under the scene + // index plugin parent rprim. If not unique, add an + // incrementing numerical suffix until it is. + const auto sceneIndexPluginPath = SdfPath::AbsoluteRootPath().AppendChild(sceneIndexPluginId); + const auto newName = uniqueChildName( + _renderIndexProxy->GetMergingSceneIndex(), + sceneIndexPluginPath, + SanitizeNameForSdfPath(dependNodeFn.name().asChar()) + ); + + registration->sceneIndexPathPrefix = sceneIndexPluginPath.AppendChild(newName); + + // Because the path interface scene index must be the last one + // in the chain, add the prefixing scene index here, instead of + // relying on the render index proxy doing it for us. + auto pfsi = HdPrefixingSceneIndex::New( registration->pluginSceneIndex, - "HdSceneIndexBase::AppendSceneIndex failed to create %s scene index from given " - "node type.", - sceneIndexPluginName.c_str())) { - - MStatus status; - MDagPath dagPath(MDagPath::getAPathTo(dagNode, &status)); - if (TF_VERIFY(status == MS::kSuccess, "Incapable of finding dag path to given node")) { - registration->dagNode = MObjectHandle(dagNode); - // Construct the scene index path prefix appended to each rprim created by it. - // It is composed of the "scene index plugin's name" + "dag node name" + - // "disambiguator" The dag node name disambiguator is necessary in situation - // where node name isn't unique and may clash with other node defined by the - // same plugin. - std::string dependNodeNameString (dependNodeFn.name().asChar()); - SanitizeNameForSdfPath(dependNodeNameString); - - registration->sceneIndexPathPrefix = - SdfPath::AbsoluteRootPath() - .AppendPath(SdfPath(sceneIndexPluginName)) - .AppendPath(SdfPath(dependNodeNameString - + (dependNodeFn.hasUniqueName() - ? "" - : "__" + std::to_string(_incrementedCounterDisambiguator++)))); - - registration->rootSceneIndex = registration->pluginSceneIndex; - #if PXR_VERSION < 2308 - // HYDRA-179 - // Inject TerminalsResolvingSceneIndex to get Hydra to handle material bindings. - // A simple string replacement for Hydra to identify the terminals based on the render context. - HdSceneIndexBaseRefPtr outSceneIndex = _AppendTerminalRenamingSceneIndex(registration->pluginSceneIndex); - // Sanity check - registration->rootSceneIndex = outSceneIndex ? outSceneIndex : registration->pluginSceneIndex; - #endif // PXR_VERSION - - // Because the path interface scene index must be the last one - // in the chain, add the prefixing scene index here, instead of - // relying on the render index proxy doing it for us. - registration->rootSceneIndex = HdPrefixingSceneIndex::New( - registration->rootSceneIndex, - registration->sceneIndexPathPrefix); - - registration->rootSceneIndex = PathInterfaceSceneIndex::New( - registration->rootSceneIndex, - registration->sceneIndexPathPrefix); - - // By inserting the scene index was inserted into the render index using a custom - // prefix, the chosen prefix will be prepended to rprims tied to that scene index - // automatically. - constexpr bool needsPrefixing = false; - _renderIndexProxy->InsertSceneIndex( - registration->rootSceneIndex, - registration->sceneIndexPathPrefix, needsPrefixing); - static SdfPath maya126790Workaround("maya126790Workaround"); - registration->pluginSceneIndex->GetPrim(maya126790Workaround); - - // Add registration record if everything succeeded - _registrations.insert({ registration->sceneIndexPathPrefix, registration }); - _registrationsByObjectHandle.insert({ registration->dagNode, registration }); - } + registration->sceneIndexPathPrefix); + + registration->rootSceneIndex = PathInterfaceSceneIndex::New( + pfsi, + registration->sceneIndexPathPrefix, + Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath)) + ); + + constexpr bool needsPrefixing = false; + _renderIndexProxy->InsertSceneIndex( + registration->rootSceneIndex, + registration->sceneIndexPathPrefix, needsPrefixing); + static SdfPath maya126790Workaround("maya126790Workaround"); + registration->pluginSceneIndex->GetPrim(maya126790Workaround); + + // Add registration record if everything succeeded + _registrations.insert({ registration->sceneIndexPathPrefix, registration }); + _registrationsByObjectHandle.insert({ registration->dagNode, registration }); } } } diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.h b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.h index 5de7b4f835..dd19e7ebff 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.h @@ -113,8 +113,6 @@ class MayaHydraSceneIndexRegistry }; std::unordered_map _registrationsByObjectHandle; - - std::atomic_int _incrementedCounterDisambiguator { 0 }; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index 94f297ff34..30099275f4 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -19,6 +19,7 @@ set(TEST_SCRIPT_FILES cpp/testMayaSceneFlattening.py cpp/testMayaUsdUfeItems.py cpp/testMergingSceneIndex.py + cpp/testPathInterface.py cpp/testSelectionSceneIndex.py cpp/testWireframeSelectionHighlightSceneIndex.py cpp/testFlowViewportAPIViewportInformation.py diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt index d2dc3448ef..55fca6c433 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(${TARGET_NAME} testMayaSceneFlattening.cpp testMayaUsdUfeItems.cpp testMergingSceneIndex.cpp + testPathInterface.cpp testSelectionSceneIndex.cpp testUtils.cpp testWireframeSelectionHighlightSceneIndex.cpp diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/mayaHydraCppTestsCmd.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/mayaHydraCppTestsCmd.cpp index d3b8a526d4..7a470668c5 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/mayaHydraCppTestsCmd.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/mayaHydraCppTestsCmd.cpp @@ -13,6 +13,7 @@ // limitations under the License. // #include "mayaHydraCppTestsCmd.h" +#include "testUtils.h" #include #include #include @@ -25,31 +26,45 @@ namespace constexpr auto _filter = "-f"; constexpr auto _filterLong = "-filter"; -} - -MSyntax mayaHydraCppTestCmd::createSyntax() -{ - MSyntax syntax; - syntax.addFlag(_filter, _filterLong, MSyntax::kString); - return syntax; -} - -std::vector constructGoogleTestArgs(const MArgDatabase& database) +MStringArray constructGoogleTestArgs(const MArgDatabase& database) { - std::vector args; - args.emplace_back("mayahydra_tests"); - MString filter = "*"; if (database.isFlagSet("-f")) { if (database.getFlagArgument("-f", 0, filter)) { } } + MStringArray args; + MStatus status = database.getObjects(args); + CHECK_MSTATUS_AND_RETURN(status, MStringArray()); + + // As per https://github.com/google/googletest/issues/3395 + // the InitGoogleTest() check verifies that the argv passed to it has at + // least one argument, otherwise it emits the following warning: + // + // IMPORTANT NOTICE - DO NOT IGNORE: + // This test program did NOT call testing::InitGoogleTest() before calling RUN_ALL_TESTS(). This is INVALID. Soon Google Test will start to enforce the valid usage. Please fix it ASAP, or IT WILL START TO FAIL. + // + // Therefore, add a dummy argument if required. + if (args.length() == 0) { + args.append("dummy"); + } + ::testing::GTEST_FLAG(filter) = filter.asChar(); return args; } +} + +MSyntax mayaHydraCppTestCmd::createSyntax() +{ + MSyntax syntax; + syntax.addFlag(_filter, _filterLong, MSyntax::kString); + syntax.setObjectType(MSyntax::kStringObjects); + return syntax; +} + MStatus mayaHydraCppTestCmd::doIt( const MArgList& args ) { MStatus status; @@ -58,16 +73,17 @@ MStatus mayaHydraCppTestCmd::doIt( const MArgList& args ) return status; } - std::vector arguments = constructGoogleTestArgs(db); + auto arguments = constructGoogleTestArgs(db); - char** argv = new char*[arguments.size()]; - int32_t argc(arguments.size()); + char** argv = new char*[arguments.length()]; + int32_t argc(arguments.length()); for (int32_t i = 0; i < argc; ++i) { - argv[i] = (char*)arguments[i].c_str(); + argv[i] = (char*)arguments[i].asChar(); } // By default, if no filter flag is given, all tests will run ::testing::InitGoogleTest(&argc, argv); + MayaHydra::setTestingArgs(argc, argv); if (RUN_ALL_TESTS() == 0 && ::testing::UnitTest::GetInstance()->test_to_run_count() > 0) { MGlobal::displayInfo("This test passed."); return MS::kSuccess; @@ -89,4 +105,4 @@ MStatus uninitializePlugin( MObject obj ) MFnPlugin plugin( obj ); plugin.deregisterCommand( "mayaHydraCppTest" ); return MS::kSuccess; -} \ No newline at end of file +} diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp new file mode 100644 index 0000000000..6bc7ee1b03 --- /dev/null +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp @@ -0,0 +1,87 @@ +// Copyright 2023 Autodesk +// +// 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. +// + +#include "testUtils.h" + +#include + +#include + +#include + +#include + +using namespace MAYAHYDRA_NS; + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace { + +SdfPath getArgSceneIndexPath(const Fvp::SelectionSceneIndexRefPtr& snSi) +{ + // Object path string is in command line arguments. + auto [argc, argv] = getTestingArgs(); + + // Get the application data model path to the selected object. + const auto mayaPath = Ufe::PathString::path(argv[0]); + + // Translate the application path into a scene index path. + return snSi->SceneIndexPath(mayaPath); +} + +Fvp::SelectionSceneIndexRefPtr getSelectionSceneIndex() +{ + const auto& sceneIndices = GetTerminalSceneIndices(); + auto isFvpSelectionSceneIndex = SceneIndexDisplayNamePred( + "Flow Viewport Selection Scene Index"); + auto selectionSiBase = findSceneIndexInTree( + sceneIndices.front(), isFvpSelectionSceneIndex); + return TfDynamic_cast(selectionSiBase); +} + +} + +TEST(TestPathInterface, testSceneIndices) +{ + const auto& sceneIndices = GetTerminalSceneIndices(); + auto childPrims = sceneIndices.front()->GetChildPrimPaths(SdfPath("/MayaUsdProxyShapeMayaNodeSceneIndexPlugin")); + ASSERT_EQ(childPrims.size(), 3u); +} + +TEST(TestPathInterface, testSelected) +{ + // Get the Flow Viewport selection scene index. + auto snSi = getSelectionSceneIndex(); + + // Selected object path string is in command line arguments. + // Get it and translate it into a scene index path. + const auto sceneIndexPath = getArgSceneIndexPath(snSi); + + // Confirm the object is selected in scene index scene. + ASSERT_TRUE(snSi->IsFullySelected(sceneIndexPath)); +} + +TEST(TestPathInterface, testUnselected) +{ + // Get the Flow Viewport selection scene index. + auto snSi = getSelectionSceneIndex(); + + // Selected object path string is in command line arguments. + // Get it and translate it into a scene index path. + const auto sceneIndexPath = getArgSceneIndexPath(snSi); + + // Confirm the object is selected in scene index scene. + ASSERT_FALSE(snSi->IsFullySelected(sceneIndexPath)); +} diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py new file mode 100644 index 0000000000..f522b6c30c --- /dev/null +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py @@ -0,0 +1,123 @@ +# Copyright 2023 Autodesk +# +# 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 pxr.Tf + +import fixturesUtils +import mtohUtils + +import ufe + +from testUtils import PluginLoaded + +import unittest + +import sys + +class TestUsdStageLayerMuting(mtohUtils.MayaHydraBaseTestCase): + # MayaHydraBaseTestCase.setUpClass requirement. + _file = __file__ + + CUBE_PRIM_PATH = "/cube" + + def cubeAppPath(self, stageIndex): + return self.ps[stageIndex] + ',' + self.CUBE_PRIM_PATH + + def setupScene(self): + import mayaUsd + import mayaUsd_createStageWithNewLayer + from pxr import UsdGeom + + self.setHdStormRenderer() + cmds.refresh() + + # Create multiple USD stages. As of 18-Dec-2023, with Arnold loaded, + # initial stage creation fails with the following exception: + # pxr.Tf.ErrorException: + # Error in 'pxrInternal_v0_23__pxrReserved__::UsdImagingAdapterRegistry' at line 146 in file S:\jenkins\workspace\ECP\ecg-usd-build\ecg-usd-full-python3.11-windows\ecg-usd-build\usd\pxr\usdImaging\usdImaging\adapterRegistry.cpp : '[PluginDiscover] A prim adapter for primType 'GeometryLight' already exists! Overriding prim adapters at runtime is not supported. The last discovered adapter (ArnoldMeshLightAdapter) will be used. The previously discovered adapter (UsdImagingGeometryLightAdapter) will be discarded.' + # + # Loading the mayaUsdPlugin is not sufficient to cause the issue. + # Fixed by the Arnold team in ARNOLD-14437, entered for maya-hydra + # integration as HYDRA-546, remove workaround when HYDRA-546 is fixed. + for i in range(3): + try: + mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + except pxr.Tf.ErrorException: + pass + + self.ps = cmds.ls(type='mayaUsdProxyShape', l=True) + + for i in range(3): + stage = mayaUsd.lib.GetPrim(self.ps[i]).GetStage() + + # Define a cube prim in the stage (implicit surface) + UsdGeom.Cube.Define(stage, self.CUBE_PRIM_PATH) + + cmds.refresh() + + @unittest.skipUnless(mtohUtils.checkForMayaUsdPlugin(), "Requires Maya USD Plugin.") + def test_PathInterface(self): + self.setupScene() + with PluginLoaded('mayaHydraCppTests'): + + #------------------------------------------------------------------ + # Test multiple stages creating children under scene index prefix + #------------------------------------------------------------------ + + # Confirm that the number of scene indices under the top-level + # prim representing the plugin matches the number of proxy shapes. + cmds.mayaHydraCppTest(f="TestPathInterface.testSceneIndices") + + #------------------------------------------------------------------ + # Test that selection works for multiple proxy shapes. + #------------------------------------------------------------------ + + # Select the cube in one of the stages, confirm it's selected + # in Hydra, and that the other cubes are not. + for selected in range(3): + cmds.select(self.cubeAppPath(selected)) + self.assertEqual(self.cubeAppPath(selected), cmds.ls(sl=True, ufe=True)[0]) + for i in range(3): + cmds.mayaHydraCppTest(self.cubeAppPath(i), f="TestPathInterface.testSelected" if i==selected else "TestPathInterface.testUnselected") + + #------------------------------------------------------------------ + # Test that selection works after proxy shape rename. + #------------------------------------------------------------------ + + # Rename one of the proxy shape transforms, and select its + # descendant cube. The cube should be selected in the Hydra scene. + result = cmds.rename(self.ps[0], 'renamedShape') + xform = ufe.PathString.string(ufe.PathString.path(self.ps[0]).pop()) + self.ps[0] = xform + '|' + result + cmds.select(self.cubeAppPath(0)) + self.assertEqual(self.cubeAppPath(0), cmds.ls(sl=True, ufe=True)[0]) + + cmds.mayaHydraCppTest(self.cubeAppPath(0), f="TestPathInterface.testSelected") + + #------------------------------------------------------------------ + # Test that selection works after proxy shape reparent. + #------------------------------------------------------------------ + + # Reparent the proxy shape transform + cmds.group(xform) + self.ps[0] = '|group1' + self.ps[0] + cmds.select(self.cubeAppPath(0)) + self.assertEqual(self.cubeAppPath(0), cmds.ls(sl=True, ufe=True)[0]) + + cmds.mayaHydraCppTest(self.cubeAppPath(0), f="TestPathInterface.testSelected") + +if __name__ == '__main__': + fixturesUtils.runTests(globals()) diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp index b1d244adbc..d06cf2ed07 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.cpp @@ -29,6 +29,10 @@ #include +namespace { +std::pair testingArgs{0, nullptr}; +} + PXR_NAMESPACE_OPEN_SCOPE // Bring the MayaHydra namespace into scope. // The following code currently lives inside the pxr namespace, but it would make more sense to @@ -241,3 +245,17 @@ HdSceneIndexBaseRefPtr findSceneIndexInTree( } PXR_NAMESPACE_CLOSE_SCOPE + +namespace MAYAHYDRA_NS_DEF { + +void setTestingArgs(int argc, char** argv) +{ + testingArgs = {argc, argv}; +} + +std::pair getTestingArgs() +{ + return testingArgs; +} + +} diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h index 3c255c02ba..f24fe62cab 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUtils.h @@ -17,6 +17,8 @@ #ifndef MAYAHYDRA_TEST_UTILS_H #define MAYAHYDRA_TEST_UTILS_H +#include + #include #include #include @@ -288,4 +290,27 @@ class SceneIndexNotificationsAccumulator : public HdSceneIndexObserver PXR_NAMESPACE_CLOSE_SCOPE +namespace MAYAHYDRA_NS_DEF { + +/** + * @brief Set global command-line arguments for use in tests. + * + * Provide argc, argv access for GoogleTest unit tests. + * + * @param[in] argc The count of arguments in argv. + * @param[in] argv Argument character strings. + */ +void setTestingArgs(int argc, char** argv); + +/** + * @brief Get global command-line arguments for use in tests. + * + * Provide argc, argv access for GoogleTest unit tests. + * + * @return argc, argv. + */ +std::pair getTestingArgs(); + +} + #endif // MAYAHYDRA_TEST_UTILS_H From cf43f309ed229ea5d7b78925c6cb9b5f7a12bc12 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 15 Feb 2024 16:38:14 -0500 Subject: [PATCH 2/5] Factored out common from _AddSceneIndexForNode. --- .../sceneIndex/registration.cpp | 216 ++++++++---------- 1 file changed, 96 insertions(+), 120 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp index 9900ecb56e..bdd2d1ac31 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp @@ -277,12 +277,44 @@ bool MayaHydraSceneIndexRegistry::_RemoveSceneIndexForNode(const MObject& dagNod return false; } -#ifdef MAYAHYDRALIB_MAYAUSDAPI_ENABLED void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) { + constexpr char kSceneIndexPluginSuffix[] = {"_MayaPlugin"}; + const MayaHydraSceneIndexRegistrationPtr registration(new MayaHydraSceneIndexRegistration()); + MFnDependencyNode dependNodeFn(dagNode); + // To match plugin TfType registration, name must begin with upper case. + const std::string sceneIndexPluginName([&](){ + std::string name = dependNodeFn.typeName().asChar(); + name[0] = toupper(name[0]); + name += kSceneIndexPluginSuffix; + return name;}()); + const TfToken sceneIndexPluginId(sceneIndexPluginName); + + MStatus status; + MDagPath dagPath(MDagPath::getAPathTo(dagNode, &status)); + if (!TF_VERIFY(status == MS::kSuccess, "Unable to find Dag path to given node")) { + return; + } + + registration->dagNode = MObjectHandle(dagNode); + + // Create a unique scene index path prefix by starting with the + // Dag node name, and checking for uniqueness under the scene + // index plugin parent rprim. If not unique, add an + // incrementing numerical suffix until it is. + const auto sceneIndexPluginPath = SdfPath::AbsoluteRootPath().AppendChild(sceneIndexPluginId); + const auto newName = uniqueChildName( + _renderIndexProxy->GetMergingSceneIndex(), + sceneIndexPluginPath, + SanitizeNameForSdfPath(dependNodeFn.name().asChar()) + ); + + registration->sceneIndexPathPrefix = sceneIndexPluginPath.AppendChild(newName); + +#ifdef MAYAHYDRALIB_MAYAUSDAPI_ENABLED + //We receive only dag nodes of type MayaUsdProxyShapeNode MAYAUSDAPI_NS::ProxyStage proxyStage(dagNode); - MayaHydraSceneIndexRegistrationPtr registration(new MayaHydraSceneIndexRegistration()); //Add the usdimaging stage scene index chain as a data producer scene index in flow viewport @@ -298,81 +330,42 @@ void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) createInfo.stage = stage;//Add the stage to the creation parameters } - MStatus status; - MDagPath dagPath(MDagPath::getAPathTo(dagNode, &status)); - if (TF_VERIFY(status == MS::kSuccess, "Incapable of finding dag path to given node")) { - registration->dagNode = MObjectHandle(dagNode); - // Construct the scene index path prefix appended to each rprim created by it. - // It is composed of the "scene index plugin's name" + "dag node name" + - // "disambiguator" The dag node name disambiguator is necessary in situation - // where node name isn't unique and may clash with other node defined by the - // same plugin. - MFnDependencyNode dependNodeFn(dagNode); - std::string dependNodeNameString (dependNodeFn.name().asChar()); - SanitizeNameForSdfPath(dependNodeNameString); - - registration->sceneIndexPathPrefix = - SdfPath::AbsoluteRootPath() - .AppendPath(SdfPath(dependNodeNameString - + (dependNodeFn.hasUniqueName() - ? "" - : "__" + std::to_string(_incrementedCounterDisambiguator++)))); - - - //We will get the following scene indices from Fvp::DataProducerSceneIndexInterfaceImp::get().addUsdStageSceneIndex - HdSceneIndexBaseRefPtr finalSceneIndex = nullptr; - UsdImagingStageSceneIndexRefPtr stageSceneIndex = nullptr; - - PXR_NS::FVP_NS_DEF::DataProducerSceneIndexDataBaseRefPtr dataProducerSceneIndexData = - Fvp::DataProducerSceneIndexInterfaceImp::get().addUsdStageSceneIndex(createInfo, finalSceneIndex, stageSceneIndex, - registration->sceneIndexPathPrefix, (void*)&dagNode); - if (nullptr == dataProducerSceneIndexData || nullptr == finalSceneIndex || nullptr == stageSceneIndex){ - TF_CODING_ERROR("Error (nullptr == dataProducerSceneIndexData || nullptr == finalSceneIndex || nullptr == stageSceneIndex) !"); - } + //We will get the following scene indices from Fvp::DataProducerSceneIndexInterfaceImp::get().addUsdStageSceneIndex + HdSceneIndexBaseRefPtr finalSceneIndex = nullptr; + UsdImagingStageSceneIndexRefPtr stageSceneIndex = nullptr; + + PXR_NS::FVP_NS_DEF::DataProducerSceneIndexDataBaseRefPtr dataProducerSceneIndexData = + Fvp::DataProducerSceneIndexInterfaceImp::get().addUsdStageSceneIndex(createInfo, finalSceneIndex, stageSceneIndex, + registration->sceneIndexPathPrefix, (void*)&dagNode); + if (nullptr == dataProducerSceneIndexData || nullptr == finalSceneIndex || nullptr == stageSceneIndex){ + TF_CODING_ERROR("Error (nullptr == dataProducerSceneIndexData || nullptr == finalSceneIndex || nullptr == stageSceneIndex) !"); + } - //Create maya usd proxy shape scene index, since this scene index contains maya data, it cannot be added by the flow viewport API - auto mayaUsdProxyShapeSceneIndex = MAYAHYDRA_NS_DEF::MayaUsdProxyShapeSceneIndex::New(proxyStage, finalSceneIndex, stageSceneIndex, MObjectHandle(dagNode)); - registration->pluginSceneIndex = mayaUsdProxyShapeSceneIndex; - registration->interpretRprimPathFn = &(MAYAHYDRA_NS_DEF::MayaUsdProxyShapeSceneIndex::InterpretRprimPath); - mayaUsdProxyShapeSceneIndex->Populate(); - - registration->rootSceneIndex = registration->pluginSceneIndex; - - //Add the PathInterfaceSceneIndex which must be the last scene index, it is used for selection highlighting - registration->rootSceneIndex = PathInterfaceSceneIndex::New( - registration->rootSceneIndex, - registration->sceneIndexPathPrefix); - - //Set the chain back into the dataProducerSceneIndexData in both members - dataProducerSceneIndexData->SetDataProducerSceneIndex(registration->rootSceneIndex); - dataProducerSceneIndexData->SetDataProducerLastSceneIndexChain(registration->rootSceneIndex); - - //Add this chain scene index to the render index proxy from all viewports - const bool bRes = Fvp::DataProducerSceneIndexInterfaceImp::get().addUsdStageDataProducerSceneIndexDataBaseToAllViewports(dataProducerSceneIndexData); - if (false == bRes){ - TF_CODING_ERROR("Fvp::DataProducerSceneIndexInterfaceImp::get().addDataProducerSceneIndex returned false !"); - } - - // Add registration record if everything succeeded - _registrations.insert({ registration->sceneIndexPathPrefix, registration }); - _registrationsByObjectHandle.insert({ registration->dagNode, registration }); + //Create maya usd proxy shape scene index, since this scene index contains maya data, it cannot be added by the flow viewport API + auto mayaUsdProxyShapeSceneIndex = MAYAHYDRA_NS_DEF::MayaUsdProxyShapeSceneIndex::New(proxyStage, finalSceneIndex, stageSceneIndex, MObjectHandle(dagNode)); + registration->pluginSceneIndex = mayaUsdProxyShapeSceneIndex; + registration->interpretRprimPathFn = &(MAYAHYDRA_NS_DEF::MayaUsdProxyShapeSceneIndex::InterpretRprimPath); + mayaUsdProxyShapeSceneIndex->Populate(); + + registration->rootSceneIndex = registration->pluginSceneIndex; + + //Add the PathInterfaceSceneIndex which must be the last scene index, it is used for selection highlighting + registration->rootSceneIndex = PathInterfaceSceneIndex::New( + registration->rootSceneIndex, + registration->sceneIndexPathPrefix, + Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath))); + + //Set the chain back into the dataProducerSceneIndexData in both members + dataProducerSceneIndexData->SetDataProducerSceneIndex(registration->rootSceneIndex); + dataProducerSceneIndexData->SetDataProducerLastSceneIndexChain(registration->rootSceneIndex); + + //Add this chain scene index to the render index proxy from all viewports + const bool bRes = Fvp::DataProducerSceneIndexInterfaceImp::get().addUsdStageDataProducerSceneIndexDataBaseToAllViewports(dataProducerSceneIndexData); + if (false == bRes){ + TF_CODING_ERROR("Fvp::DataProducerSceneIndexInterfaceImp::get().addDataProducerSceneIndex returned false !"); } -} + #else -namespace -{ - constexpr char kSceneIndexPluginSuffix[] = {"MayaNodeSceneIndexPlugin"}; -} -void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) -{ - MFnDependencyNode dependNodeFn(dagNode); - // To match plugin TfType registration, name must begin with upper case. - const std::string sceneIndexPluginName([&](){ - std::string name = dependNodeFn.typeName().asChar(); - name[0] = toupper(name[0]); - name += kSceneIndexPluginSuffix; - return name;}()); - const TfToken sceneIndexPluginId(sceneIndexPluginName); static HdSceneIndexPluginRegistry& sceneIndexPluginRegistry = HdSceneIndexPluginRegistry::GetInstance(); @@ -392,7 +385,7 @@ void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) TfToken("object"), TfToken("version"), TfToken("interpretRprimPath") }; constexpr int kDataSourceNumEntries = sizeof(sDataSourceEntryNames) / sizeof(TfToken); - MayaHydraSceneIndexRegistrationPtr registration(new MayaHydraSceneIndexRegistration()); + HdDataSourceBaseHandle values[] { MObjectDataSource::New(dagNode), MayaHydraVersionDataSource::New(MAYAHYDRA_API_VERSION), @@ -405,58 +398,41 @@ void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) sceneIndexPluginId, nullptr, HdRetainedContainerDataSource::New(kDataSourceNumEntries, sDataSourceEntryNames, values)); - if (TF_VERIFY( + + if (!TF_VERIFY( registration->pluginSceneIndex, "MayaHydraSceneIndexRegistry::_AddSceneIndexForNode failed to create %s scene index from given " "node type.", sceneIndexPluginName.c_str())) { - - MStatus status; - MDagPath dagPath(MDagPath::getAPathTo(dagNode, &status)); - if (TF_VERIFY(status == MS::kSuccess, "Unable of finding Dag path to given node")) { - registration->dagNode = MObjectHandle(dagNode); - - // Create a unique scene index path prefix by starting with the - // Dag node name, and checking for uniqueness under the scene - // index plugin parent rprim. If not unique, add an - // incrementing numerical suffix until it is. - const auto sceneIndexPluginPath = SdfPath::AbsoluteRootPath().AppendChild(sceneIndexPluginId); - const auto newName = uniqueChildName( - _renderIndexProxy->GetMergingSceneIndex(), - sceneIndexPluginPath, - SanitizeNameForSdfPath(dependNodeFn.name().asChar()) - ); - - registration->sceneIndexPathPrefix = sceneIndexPluginPath.AppendChild(newName); - - // Because the path interface scene index must be the last one - // in the chain, add the prefixing scene index here, instead of - // relying on the render index proxy doing it for us. - auto pfsi = HdPrefixingSceneIndex::New( - registration->pluginSceneIndex, - registration->sceneIndexPathPrefix); - - registration->rootSceneIndex = PathInterfaceSceneIndex::New( - pfsi, - registration->sceneIndexPathPrefix, - Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath)) - ); - - constexpr bool needsPrefixing = false; - _renderIndexProxy->InsertSceneIndex( - registration->rootSceneIndex, - registration->sceneIndexPathPrefix, needsPrefixing); - static SdfPath maya126790Workaround("maya126790Workaround"); - registration->pluginSceneIndex->GetPrim(maya126790Workaround); - - // Add registration record if everything succeeded - _registrations.insert({ registration->sceneIndexPathPrefix, registration }); - _registrationsByObjectHandle.insert({ registration->dagNode, registration }); - } + return; } -} + + // Because the path interface scene index must be the last one + // in the chain, add the prefixing scene index here, instead of + // relying on the render index proxy doing it for us. + auto pfsi = HdPrefixingSceneIndex::New( + registration->pluginSceneIndex, + registration->sceneIndexPathPrefix); + + registration->rootSceneIndex = PathInterfaceSceneIndex::New( + pfsi, + registration->sceneIndexPathPrefix, + Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath)) + ); + + constexpr bool needsPrefixing = false; + _renderIndexProxy->InsertSceneIndex( + registration->rootSceneIndex, + registration->sceneIndexPathPrefix, needsPrefixing); + static SdfPath maya126790Workaround("maya126790Workaround"); + registration->pluginSceneIndex->GetPrim(maya126790Workaround); #endif //MAYAHYDRALIB_MAYAUSDAPI_ENABLED + // Add registration record if everything succeeded + _registrations.insert({ registration->sceneIndexPathPrefix, registration }); + _registrationsByObjectHandle.insert({ registration->dagNode, registration }); +} + void MayaHydraSceneIndexRegistry::_SceneIndexNodeAddedCallback(MObject& dagNode, void* clientData) { if (dagNode.isNull() || dagNode.apiType() != MFn::kPluginShape) From 83fbcb88d4d70f16513472df91a1835ab2f0fb1f Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Fri, 16 Feb 2024 14:32:42 -0500 Subject: [PATCH 3/5] Updated prefix name. --- lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp | 2 +- test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp index eedd891784..03aee0b975 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp @@ -285,7 +285,7 @@ bool MayaHydraSceneIndexRegistry::_RemoveSceneIndexForNode(const MObject& dagNod void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) { - constexpr char kSceneIndexPluginSuffix[] = {"_MayaPlugin"}; + constexpr char kSceneIndexPluginSuffix[] = {"_PluginNode"}; const MayaHydraSceneIndexRegistrationPtr registration(new MayaHydraSceneIndexRegistration()); MFnDependencyNode dependNodeFn(dagNode); // To match plugin TfType registration, name must begin with upper case. diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp index 6bc7ee1b03..b31e28004a 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp @@ -56,7 +56,7 @@ Fvp::SelectionSceneIndexRefPtr getSelectionSceneIndex() TEST(TestPathInterface, testSceneIndices) { const auto& sceneIndices = GetTerminalSceneIndices(); - auto childPrims = sceneIndices.front()->GetChildPrimPaths(SdfPath("/MayaUsdProxyShapeMayaNodeSceneIndexPlugin")); + auto childPrims = sceneIndices.front()->GetChildPrimPaths(SdfPath("/MayaUsdProxyShape_PluginNode")); ASSERT_EQ(childPrims.size(), 3u); } From ba3935c11cc17f9c3efb75fac78a0da543308f30 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Wed, 21 Feb 2024 16:33:02 -0500 Subject: [PATCH 4/5] Addressed code review comments. --- lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp | 8 +++----- .../mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp | 6 +++--- .../mayaUsd/render/mayaToHydra/cpp/testPathInterface.py | 6 ++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp index 03aee0b975..85c17a2a4d 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/registration.cpp @@ -353,15 +353,13 @@ void MayaHydraSceneIndexRegistry::_AddSceneIndexForNode(MObject& dagNode) registration->interpretRprimPathFn = &(MAYAHYDRA_NS_DEF::MayaUsdProxyShapeSceneIndex::InterpretRprimPath); mayaUsdProxyShapeSceneIndex->Populate(); - registration->rootSceneIndex = registration->pluginSceneIndex; - - registration->rootSceneIndex = HdPrefixingSceneIndex::New( - registration->rootSceneIndex, + auto pfsi = HdPrefixingSceneIndex::New( + registration->pluginSceneIndex, registration->sceneIndexPathPrefix); //Add the PathInterfaceSceneIndex which must be the last scene index, it is used for selection highlighting registration->rootSceneIndex = PathInterfaceSceneIndex::New( - registration->rootSceneIndex, + pfsi, registration->sceneIndexPathPrefix, Ufe::Path(UfeExtensions::dagPathToUfePathSegment(dagPath))); diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp index b31e28004a..cfefc90600 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.cpp @@ -1,4 +1,4 @@ -// Copyright 2023 Autodesk +// Copyright 2024 Autodesk // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -78,10 +78,10 @@ TEST(TestPathInterface, testUnselected) // Get the Flow Viewport selection scene index. auto snSi = getSelectionSceneIndex(); - // Selected object path string is in command line arguments. + // Unselected object path string is in command line arguments. // Get it and translate it into a scene index path. const auto sceneIndexPath = getArgSceneIndexPath(snSi); - // Confirm the object is selected in scene index scene. + // Confirm the object is not selected in scene index scene. ASSERT_FALSE(snSi->IsFullySelected(sceneIndexPath)); } diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py index f522b6c30c..07b9ec053c 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testPathInterface.py @@ -1,4 +1,4 @@ -# Copyright 2023 Autodesk +# Copyright 2024 Autodesk # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,9 +25,7 @@ import unittest -import sys - -class TestUsdStageLayerMuting(mtohUtils.MayaHydraBaseTestCase): +class TestPathInterface(mtohUtils.MayaHydraBaseTestCase): # MayaHydraBaseTestCase.setUpClass requirement. _file = __file__ From 54d36d8052d1cce5e2f2e6fc19fedceb6b0f9cd5 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Mon, 26 Feb 2024 11:29:14 -0500 Subject: [PATCH 5/5] Fixed incorrect Python test import in build without mayaUsd. --- test/lib/mayaUsd/render/mayaToHydra/testStageVariants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lib/mayaUsd/render/mayaToHydra/testStageVariants.py b/test/lib/mayaUsd/render/mayaToHydra/testStageVariants.py index 4fd09d3842..51341cdc8d 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/testStageVariants.py +++ b/test/lib/mayaUsd/render/mayaToHydra/testStageVariants.py @@ -20,7 +20,6 @@ import unittest import testUtils import mayaUtils -import usdUtils import ufe class TestStageVariants(mtohUtils.MtohTestCase): #Subclassing mtohUtils.MtohTestCase to be able to call self.assertSnapshotClose @@ -33,6 +32,8 @@ class TestStageVariants(mtohUtils.MtohTestCase): #Subclassing mtohUtils.MtohTest @unittest.skipUnless(mtohUtils.checkForMayaUsdPlugin(), "Requires Maya USD Plugin.") def test_UsdStageVariants(self): from mayaUsd import lib as mayaUsdLib + # usdUtils imports mayaUsd.ufe + import usdUtils cmds.file(new=True, force=True) testFile = mayaUtils.openTestScene(