From bd711757eca6cdd5991948ee3410401e55b5ef5c Mon Sep 17 00:00:00 2001 From: Sean Donnelly <23455376+seando-adsk@users.noreply.github.com> Date: Thu, 3 Sep 2020 17:03:26 -0400 Subject: [PATCH 1/2] MAYA-106188 - As a user, I would like to activate and deactivate prims * Implemented new UFE interface UIInfoHandler. * Added support for child filtering with new Ufe::Hierarchy method "filteredChildren" and Ufe::HierarchyHandler method "childFilter". In USD we support a single child filter for "Inactive Prims". * Added context menu for "{Active/Deactivate} Prim". * Added test cases for new interface, new methods and new menu. --- lib/mayaUsd/ufe/CMakeLists.txt | 7 ++ lib/mayaUsd/ufe/Global.cpp | 9 ++ lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp | 12 +++ lib/mayaUsd/ufe/ProxyShapeHierarchy.h | 3 + .../ufe/ProxyShapeHierarchyHandler.cpp | 9 ++ lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.h | 3 + lib/mayaUsd/ufe/UsdContextOps.cpp | 41 ++++++++- lib/mayaUsd/ufe/UsdHierarchy.cpp | 49 ++++++++--- lib/mayaUsd/ufe/UsdHierarchy.h | 6 ++ lib/mayaUsd/ufe/UsdHierarchyHandler.cpp | 12 +++ lib/mayaUsd/ufe/UsdHierarchyHandler.h | 3 + lib/mayaUsd/ufe/UsdSceneItemOps.cpp | 33 +++++++- lib/mayaUsd/ufe/UsdUIInfoHandler.cpp | 81 ++++++++++++++++++ lib/mayaUsd/ufe/UsdUIInfoHandler.h | 54 ++++++++++++ test/lib/ufe/CMakeLists.txt | 2 + test/lib/ufe/testChildFilter.py | 83 +++++++++++++++++++ test/lib/ufe/testContextOps.py | 19 +++++ test/lib/ufe/testUIInfoHandler.py | 77 +++++++++++++++++ 18 files changed, 485 insertions(+), 18 deletions(-) create mode 100644 lib/mayaUsd/ufe/UsdUIInfoHandler.cpp create mode 100644 lib/mayaUsd/ufe/UsdUIInfoHandler.h create mode 100644 test/lib/ufe/testChildFilter.py create mode 100644 test/lib/ufe/testUIInfoHandler.py diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index 4e4efab1d3..c3c7327cf4 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -44,6 +44,12 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) UsdUndoCreateGroupCommand.cpp UsdUndoInsertChildCommand.cpp ) + if(UFE_PREVIEW_VERSION_NUM GREATER_EQUAL 2022) + target_sources(${PROJECT_NAME} + PRIVATE + UsdUIInfoHandler.cpp + ) + endif() endif() set(HEADERS @@ -83,6 +89,7 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) UsdContextOpsHandler.h UsdObject3d.h UsdObject3dHandler.h + UsdUIInfoHandler.h UsdUndoAddNewPrimCommand.h UsdUndoCreateGroupCommand.h UsdUndoInsertChildCommand.h diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index adbe6463bd..8453d82c16 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -36,6 +36,9 @@ #include #include #include +#if UFE_PREVIEW_VERSION_NUM >= 2022 +#include +#endif #include #else #include @@ -115,11 +118,17 @@ MStatus initialize() UFE_V2(auto usdAttributesHandler = UsdAttributesHandler::create();) UFE_V2(auto usdObject3dHandler = UsdObject3dHandler::create();) UFE_V2(auto usdContextOpsHandler = UsdContextOpsHandler::create();) +#if UFE_PREVIEW_VERSION_NUM >= 2022 + UFE_V2(auto usdUIInfoHandler = UsdUIInfoHandler::create();) +#endif g_USDRtid = Ufe::RunTimeMgr::instance().register_( kUSDRunTimeName, usdHierHandler, usdTrans3dHandler, usdSceneItemOpsHandler UFE_V2(, usdAttributesHandler, usdObject3dHandler) UFE_V2(, usdContextOpsHandler) +#if UFE_PREVIEW_VERSION_NUM >= 2022 + UFE_V2(, usdUIInfoHandler) +#endif ); #if !defined(NDEBUG) diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp index 437515b769..eb54691fd7 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp @@ -16,6 +16,7 @@ #include "ProxyShapeHierarchy.h" #include +#include #include #include @@ -144,6 +145,17 @@ Ufe::SceneItemList ProxyShapeHierarchy::children() const return children; } +#ifdef UFE_V2_FEATURES_AVAILABLE +#if UFE_PREVIEW_VERSION_NUM >= 2022 +Ufe::SceneItemList ProxyShapeHierarchy::filteredChildren(const ChildFilter& childFilter) const +{ + // Currently no child filters are supported. + assert(childFilter.empty()); + return children(); +} +#endif +#endif + Ufe::SceneItem::Ptr ProxyShapeHierarchy::parent() const { return fMayaHierarchy->parent(); diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.h b/lib/mayaUsd/ufe/ProxyShapeHierarchy.h index 4d132be25a..53ff41f592 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.h +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.h @@ -60,6 +60,9 @@ class MAYAUSD_CORE_PUBLIC ProxyShapeHierarchy : public Ufe::Hierarchy Ufe::SceneItem::Ptr sceneItem() const override; bool hasChildren() const override; Ufe::SceneItemList children() const override; +#if UFE_PREVIEW_VERSION_NUM >= 2022 + UFE_V2(Ufe::SceneItemList filteredChildren(const ChildFilter&) const override;) +#endif Ufe::SceneItem::Ptr parent() const override; #ifndef UFE_V2_FEATURES_AVAILABLE Ufe::AppendedChild appendChild(const Ufe::SceneItem::Ptr& child) override; diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp index 1dfd7f85a2..71aadb2cec 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp @@ -57,5 +57,14 @@ Ufe::SceneItem::Ptr ProxyShapeHierarchyHandler::createItem(const Ufe::Path& path return fMayaHierarchyHandler->createItem(path); } +#ifdef UFE_V2_FEATURES_AVAILABLE +#if UFE_PREVIEW_VERSION_NUM >= 2022 +Ufe::Hierarchy::ChildFilter ProxyShapeHierarchyHandler::childFilter() const +{ + return Ufe::Hierarchy::ChildFilter(); +} +#endif +#endif + } // namespace ufe } // namespace MayaUsd diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.h b/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.h index 6a325c387a..9f22307132 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.h +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.h @@ -55,6 +55,9 @@ class MAYAUSD_CORE_PUBLIC ProxyShapeHierarchyHandler : public Ufe::HierarchyHand // Ufe::HierarchyHandler overrides Ufe::Hierarchy::Ptr hierarchy(const Ufe::SceneItem::Ptr& item) const override; Ufe::SceneItem::Ptr createItem(const Ufe::Path& path) const override; +#if UFE_PREVIEW_VERSION_NUM >= 2022 + UFE_V2(Ufe::Hierarchy::ChildFilter childFilter() const override;) +#endif private: Ufe::HierarchyHandler::Ptr fMayaHierarchyHandler; diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index cf06300624..b3ba7f1b59 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -15,6 +15,8 @@ // #include "UsdContextOps.h" +#include "private/UfeNotifGuard.h" + #include #include @@ -47,6 +49,9 @@ static constexpr char kUSDVariantSetsLabel[] = "Variant Sets"; static constexpr char kUSDToggleVisibilityItem[] = "Toggle Visibility"; static constexpr char kUSDMakeVisibleLabel[] = "Make Visible"; static constexpr char kUSDMakeInvisibleLabel[] = "Make Invisible"; +static constexpr char kUSDToggleActiveStateItem[] = "Toggle Active State"; +static constexpr char kUSDActivatePrimLabel[] = "Activate Prim"; +static constexpr char kUSDDeactivatePrimLabel[] = "Deactivate Prim"; static constexpr char kUSDAddNewPrimItem[] = "Add New Prim"; static constexpr char kUSDAddNewPrimLabel[] = "Add New Prim"; static constexpr char kUSDDefPrimItem[] = "Def"; @@ -90,6 +95,33 @@ class SetVariantSelectionUndoableCommand : public Ufe::UndoableCommand const std::string fNewSelection; }; +//! \brief Undoable command for prim active state change +class ToggleActiveStateCommand : public Ufe::UndoableCommand +{ +public: + ToggleActiveStateCommand(const UsdPrim& prim) + : _prim(prim) + { + _active = _prim.IsActive(); + } + + void undo() override + { + MayaUsd::ufe::InAddOrDeleteOperation ad; + _prim.SetActive(_active); + } + + void redo() override + { + MayaUsd::ufe::InAddOrDeleteOperation ad; + _prim.SetActive(!_active); + } + +private: + UsdPrim _prim; + bool _active; +}; + const char* selectUSDFileScript = R"( global proc string SelectUSDFileForAddReference() { @@ -235,12 +267,14 @@ Ufe::ContextOps::Items UsdContextOps::getItems( items.emplace_back(Ufe::ContextItem::kSeparator); } - // Top-level items. Variant sets and visibility. Do not add for gateway type node. + // Top-level items (do not add for gateway type node): if (!fIsAGatewayType) { + // Variant sets: if (prim().HasVariantSets()) { items.emplace_back( kUSDVariantSetsItem, kUSDVariantSetsLabel, Ufe::ContextItem::kHasChildren); } + // Visibility: // If the item has a visibility attribute, add menu item to change visibility. // Note: certain prim types such as shaders & materials don't support visibility. auto attributes = Ufe::Attributes::attributes(sceneItem()); @@ -255,6 +289,8 @@ Ufe::ContextOps::Items UsdContextOps::getItems( items.emplace_back(kUSDToggleVisibilityItem, l); } } + // Prim active state: + items.emplace_back(kUSDToggleActiveStateItem, prim().IsActive() ? kUSDDeactivatePrimLabel : kUSDActivatePrimLabel); } // Top level item - Add New Prim (for all context op types). @@ -344,6 +380,9 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return visibility->setCmd( current == UsdGeomTokens->invisible ? UsdGeomTokens->inherited : UsdGeomTokens->invisible); } // Visibility + else if (itemPath[0] == kUSDToggleActiveStateItem) { + return std::make_shared(prim()); + } // ActiveState else if (!itemPath.empty() && (itemPath[0] == kUSDAddNewPrimItem)) { // Operation is to create a new prim of the type specified. if (itemPath.size() != 2u) { diff --git a/lib/mayaUsd/ufe/UsdHierarchy.cpp b/lib/mayaUsd/ufe/UsdHierarchy.cpp index a775ac289d..c7284c3d6d 100644 --- a/lib/mayaUsd/ufe/UsdHierarchy.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchy.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -41,7 +42,7 @@ #endif namespace { - UsdPrimSiblingRange filteredChildren( const UsdPrim& prim ) + UsdPrimSiblingRange getUSDFilteredChildren(const UsdPrim& prim, const Usd_PrimFlagsPredicate pred = UsdPrimDefaultPredicate) { // We need to be able to traverse down to instance proxies, so turn // on that part of the predicate, since by default, it is off. Since @@ -49,11 +50,8 @@ namespace { // GetFilteredChildren( UsdPrimDefaultPredicate ), // we will use that as the initial value. // - Usd_PrimFlagsPredicate predicate = UsdPrimDefaultPredicate; - predicate = predicate.TraverseInstanceProxies(true); - return prim.GetFilteredChildren(predicate); + return prim.GetFilteredChildren(UsdTraverseInstanceProxies(pred)); } - } MAYAUSD_NS_DEF { @@ -101,18 +99,43 @@ Ufe::SceneItem::Ptr UsdHierarchy::sceneItem() const bool UsdHierarchy::hasChildren() const { - return !filteredChildren(prim()).empty(); + return !getUSDFilteredChildren(prim()).empty(); } Ufe::SceneItemList UsdHierarchy::children() const { - // Return USD children only, i.e. children within this run-time. - Ufe::SceneItemList children; - for (auto child : filteredChildren(prim())) - { - children.emplace_back(UsdSceneItem::create(fItem->path() + child.GetName(), child)); - } - return children; + return createUFEChildList(getUSDFilteredChildren(prim())); +} + +#ifdef UFE_V2_FEATURES_AVAILABLE +#if UFE_PREVIEW_VERSION_NUM >= 2022 +Ufe::SceneItemList UsdHierarchy::filteredChildren(const ChildFilter& childFilter) const +{ + // Note: for now the only child filter flag we support is "Inactive Prims". + // See UsdHierarchyHandler::childFilter() + if ((childFilter.size() == 1) && (childFilter.front().name == "InactivePrims")) + { + // See uniqueChildName() for explanation of USD filter predicate. + Usd_PrimFlagsPredicate flags = childFilter.front().value ? UsdPrimIsDefined && !UsdPrimIsAbstract + : UsdPrimDefaultPredicate; + return createUFEChildList(getUSDFilteredChildren(prim(), flags)); + } + + UFE_LOG("Unknown child filter"); + return Ufe::SceneItemList(); +} +#endif +#endif + +Ufe::SceneItemList UsdHierarchy::createUFEChildList(const UsdPrimSiblingRange& range) const +{ + // Return UFE child list from input USD child list. + Ufe::SceneItemList children; + for (auto child : range) + { + children.emplace_back(UsdSceneItem::create(fItem->path() + child.GetName(), child)); + } + return children; } Ufe::SceneItem::Ptr UsdHierarchy::parent() const diff --git a/lib/mayaUsd/ufe/UsdHierarchy.h b/lib/mayaUsd/ufe/UsdHierarchy.h index 4577b8d9af..da01bf8437 100644 --- a/lib/mayaUsd/ufe/UsdHierarchy.h +++ b/lib/mayaUsd/ufe/UsdHierarchy.h @@ -57,6 +57,9 @@ class MAYAUSD_CORE_PUBLIC UsdHierarchy : public Ufe::Hierarchy Ufe::SceneItem::Ptr sceneItem() const override; bool hasChildren() const override; Ufe::SceneItemList children() const override; +#if UFE_PREVIEW_VERSION_NUM >= 2022 + UFE_V2(Ufe::SceneItemList filteredChildren(const ChildFilter&) const override;) +#endif Ufe::SceneItem::Ptr parent() const override; #ifndef UFE_V2_FEATURES_AVAILABLE Ufe::AppendedChild appendChild(const Ufe::SceneItem::Ptr& child) override; @@ -78,6 +81,9 @@ class MAYAUSD_CORE_PUBLIC UsdHierarchy : public Ufe::Hierarchy ) override; #endif +private: + Ufe::SceneItemList createUFEChildList(const UsdPrimSiblingRange& range) const; + private: UsdSceneItem::Ptr fItem; diff --git a/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp b/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp index 22b79db32c..80078f0ce4 100644 --- a/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp @@ -52,5 +52,17 @@ Ufe::SceneItem::Ptr UsdHierarchyHandler::createItem(const Ufe::Path& path) const return prim.IsValid() ? UsdSceneItem::create(path, prim) : nullptr; } +#ifdef UFE_V2_FEATURES_AVAILABLE +#if UFE_PREVIEW_VERSION_NUM >= 2022 +Ufe::Hierarchy::ChildFilter UsdHierarchyHandler::childFilter() const +{ + Ufe::ChildFilterFlag inactivePrims("InactivePrims", "Inactive Prims", true); + Ufe::Hierarchy::ChildFilter childFilters; + childFilters.emplace_back(inactivePrims); + return childFilters; +} +#endif +#endif + } // namespace ufe } // namespace MayaUsd diff --git a/lib/mayaUsd/ufe/UsdHierarchyHandler.h b/lib/mayaUsd/ufe/UsdHierarchyHandler.h index 8d144ce335..a1d6917d49 100644 --- a/lib/mayaUsd/ufe/UsdHierarchyHandler.h +++ b/lib/mayaUsd/ufe/UsdHierarchyHandler.h @@ -54,6 +54,9 @@ class MAYAUSD_CORE_PUBLIC UsdHierarchyHandler : public Ufe::HierarchyHandler // UsdHierarchyHandler overrides Ufe::Hierarchy::Ptr hierarchy(const Ufe::SceneItem::Ptr& item) const override; Ufe::SceneItem::Ptr createItem(const Ufe::Path& path) const override; +#if UFE_PREVIEW_VERSION_NUM >= 2022 + UFE_V2(Ufe::Hierarchy::ChildFilter childFilter() const override;) +#endif }; // UsdHierarchyHandler } // namespace ufe diff --git a/lib/mayaUsd/ufe/UsdSceneItemOps.cpp b/lib/mayaUsd/ufe/UsdSceneItemOps.cpp index 17b3159e90..4cb6d70b46 100644 --- a/lib/mayaUsd/ufe/UsdSceneItemOps.cpp +++ b/lib/mayaUsd/ufe/UsdSceneItemOps.cpp @@ -20,6 +20,21 @@ #include #include +#include + +namespace { +// Warning: If a user tries to delete a prim that is deactivated (can be localized). +static constexpr char kWarningCannotDeactivePrim[] = "Cannot deactivate \"^1s\" because it is already inactive."; + +void displayWarning(const UsdPrim& prim, const MString& fmt) +{ + MString msg, primArg(prim.GetName().GetText()); + msg.format(fmt, primArg); + MGlobal::displayWarning(msg); +} + +} + MAYAUSD_NS_DEF { namespace ufe { @@ -60,14 +75,24 @@ Ufe::SceneItem::Ptr UsdSceneItemOps::sceneItem() const Ufe::UndoableCommand::Ptr UsdSceneItemOps::deleteItemCmd() { - auto deleteCmd = UsdUndoDeleteCommand::create(prim()); - deleteCmd->execute(); - return deleteCmd; + if (prim().IsActive()) { + auto deleteCmd = UsdUndoDeleteCommand::create(prim()); + deleteCmd->execute(); + return deleteCmd; + } + + displayWarning(prim(), kWarningCannotDeactivePrim); + return nullptr; } bool UsdSceneItemOps::deleteItem() { - return prim().SetActive(false); + if (prim().IsActive()) { + return prim().SetActive(false); + } + + displayWarning(prim(), kWarningCannotDeactivePrim); + return false; } Ufe::Duplicate UsdSceneItemOps::duplicateItemCmd() diff --git a/lib/mayaUsd/ufe/UsdUIInfoHandler.cpp b/lib/mayaUsd/ufe/UsdUIInfoHandler.cpp new file mode 100644 index 0000000000..f97bd39384 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUIInfoHandler.cpp @@ -0,0 +1,81 @@ +// +// Copyright 2020 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 "UsdUIInfoHandler.h" +#include "UsdSceneItem.h" + +#include +#include + +MAYAUSD_NS_DEF { +namespace ufe { + +UsdUIInfoHandler::UsdUIInfoHandler() + : Ufe::UIInfoHandler() +{} + +UsdUIInfoHandler::~UsdUIInfoHandler() +{ +} + +/*static*/ +UsdUIInfoHandler::Ptr UsdUIInfoHandler::create() +{ + return std::make_shared(); +} + +//------------------------------------------------------------------------------ +// Ufe::UIInfoHandler overrides +//------------------------------------------------------------------------------ + +bool UsdUIInfoHandler::treeViewCellInfo(const Ufe::SceneItem::Ptr& item, Ufe::CellInfo& info) const +{ + bool changed = false; + UsdSceneItem::Ptr usdItem = std::dynamic_pointer_cast(item); +#if !defined(NDEBUG) + assert(usdItem); +#endif + if (usdItem) + { + if (!usdItem->prim().IsActive()) + { + changed = true; + info.fontStrikeout = true; + MDoubleArray outlinerInvisibleColor; + if (MGlobal::executeCommand("displayRGBColor -q \"outlinerInvisibleColor\"", outlinerInvisibleColor) && + (outlinerInvisibleColor.length() == 3)) + { + double rgb[3]; + outlinerInvisibleColor.get(rgb); + info.textFgColor.set(static_cast(rgb[0]), static_cast(rgb[1]), static_cast(rgb[2])); + } + else + { + info.textFgColor.set(0.403922f, 0.403922f, 0.403922f); + } + } + } + + return changed; +} + +std::string UsdUIInfoHandler::getLongRunTimeLabel() const +{ + return "Universal Scene Description"; +} + + +} // namespace ufe +} // namespace MayaUsd diff --git a/lib/mayaUsd/ufe/UsdUIInfoHandler.h b/lib/mayaUsd/ufe/UsdUIInfoHandler.h new file mode 100644 index 0000000000..dc633b56ba --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUIInfoHandler.h @@ -0,0 +1,54 @@ +#ifndef USDUIINFOHANDLER_H +#define USDUIINFOHANDLER_H + +// +// Copyright 2020 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 + +#include + + +MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief Interface to create a UsdUIInfoHandler interface object. +class MAYAUSD_CORE_PUBLIC UsdUIInfoHandler : public Ufe::UIInfoHandler +{ +public: + typedef std::shared_ptr Ptr; + + UsdUIInfoHandler(); + ~UsdUIInfoHandler() override; + + // Delete the copy/move constructors assignment operators. + UsdUIInfoHandler(const UsdUIInfoHandler&) = delete; + UsdUIInfoHandler& operator=(const UsdUIInfoHandler&) = delete; + UsdUIInfoHandler(UsdUIInfoHandler&&) = delete; + UsdUIInfoHandler& operator=(UsdUIInfoHandler&&) = delete; + + //! Create a UsdUIInfoHandler. + static UsdUIInfoHandler::Ptr create(); + + // Ufe::UIInfoHandler overrides + bool treeViewCellInfo(const Ufe::SceneItem::Ptr& item, Ufe::CellInfo& info) const override; + std::string getLongRunTimeLabel() const override; +}; // UsdUIInfoHandler + +} // namespace ufe +} // namespace MayaUsd + +#endif // USDUIINFOHANDLER_H diff --git a/test/lib/ufe/CMakeLists.txt b/test/lib/ufe/CMakeLists.txt index 36428ccb62..ca4aa88911 100644 --- a/test/lib/ufe/CMakeLists.txt +++ b/test/lib/ufe/CMakeLists.txt @@ -19,6 +19,7 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) list(APPEND TEST_SCRIPT_FILES testAttribute.py testAttributes.py + testChildFilter.py testComboCmd.py testContextOps.py testDuplicateCmd.py @@ -31,6 +32,7 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) testScaleCmd.py testSceneItem.py testTransform3dTranslate.py + testUIInfoHandler.py testObservableScene.py ) endif() diff --git a/test/lib/ufe/testChildFilter.py b/test/lib/ufe/testChildFilter.py new file mode 100644 index 0000000000..139fcbf1dc --- /dev/null +++ b/test/lib/ufe/testChildFilter.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +# +# Copyright 2020 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 os + +import maya.cmds as cmds + +from ufeTestUtils import mayaUtils +import ufe + +import unittest + +@unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '2022', 'ChildFilterTestCase is only available in Maya with UFE preview version 0.2.22 and greater') +class ChildFilterTestCase(unittest.TestCase): + '''Verify the ChildFilter USD implementation. + ''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + def setUp(self): + ''' Called initially to set up the Maya test environment ''' + + # Load plugins + self.assertTrue(self.pluginsLoaded) + + # Open ballset.ma scene in test-samples + mayaUtils.openGroupBallsScene() + + # Clear selection to start off + cmds.select(clear=True) + + def testFilteredChildren(self): + # Check that we have six balls + propsPath = ufe.PathString.path('|transform1|proxyShape1,/Ball_set/Props') + propsItem = ufe.Hierarchy.createItem(propsPath) + propsHier = ufe.Hierarchy.hierarchy(propsItem) + self.assertEqual(6, len(propsHier.children())) + + # Deactivate Ball_3 (which will remove it from children) + ball3Path = ufe.PathString.path('|transform1|proxyShape1,/Ball_set/Props/Ball_3') + ball3Hier = ufe.Hierarchy.createItem(ball3Path) + cmds.delete('|transform1|proxyShape1,/Ball_set/Props/Ball_3') + + # Props should now have 5 children and ball3 should not be one of them. + children = propsHier.children() + self.assertEqual(5, len(children)) + self.assertNotIn(ball3Hier, children) + + # Ensure we have one USD child filter + rid = ufe.RunTimeMgr.instance().getId('USD') + usdHierHndlr = ufe.RunTimeMgr.instance().hierarchyHandler(rid) + cf = usdHierHndlr.childFilter() + self.assertEqual(1, len(cf)) + + # Make sure the USD hierarchy handler has an inactive prims filter + self.assertEqual('InactivePrims', cf[0].name) + + # Toggle "Inactive Prims" on and get the filtered chlidren with + # Inactive Prims and verify ball3 is one of them. + cf[0].value = True + children = propsHier.filteredChildren(cf) + self.assertEqual(6, len(children)) + self.assertIn(ball3Hier, children) diff --git a/test/lib/ufe/testContextOps.py b/test/lib/ufe/testContextOps.py index b330c0f5b5..742c0743b4 100644 --- a/test/lib/ufe/testContextOps.py +++ b/test/lib/ufe/testContextOps.py @@ -79,6 +79,7 @@ def setUp(self): mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")]) self.ball35Item = ufe.Hierarchy.createItem(ball35Path) + self.ball35Prim = usdUtils.getPrimFromSceneItem(self.ball35Item) ufe.GlobalSelection.get().append(self.ball35Item) @@ -90,6 +91,7 @@ def testGetItems(self): # - visibility (for all prims with visibility attribute) # - variant sets, for those prims that have them (such as Ball_35) # - add new prim (for all prims) + # - prim activate/deactivate contextItems = self.contextOps.getItems([]) contextItemStrings = [c.item for c in contextItems] @@ -102,6 +104,7 @@ def testGetItems(self): self.assertIn('Toggle Visibility', contextItemStrings) else: self.assertNotIn('Toggle Visibility', contextItemStrings) + self.assertIn('Toggle Active State', contextItemStrings) self.assertIn('Add New Prim', contextItemStrings) # Ball_35 has a single variant set, which has children. @@ -149,6 +152,22 @@ def testDoOp(self): cmds.undo() + # Active / Deactivate Prim + cmd = self.contextOps.doOpCmd(['Toggle Active State']) + self.assertIsNotNone(cmd) + + # Initially, Ball_35 should be active. + self.assertTrue(self.ball35Prim.IsActive()) + + ufeCmd.execute(cmd) + self.assertFalse(self.ball35Prim.IsActive()) + + cmds.undo() + self.assertTrue(self.ball35Prim.IsActive()) + + cmds.redo() + self.assertFalse(self.ball35Prim.IsActive()) + # Change variant in variant set. def shadingVariant(): contextItems = self.contextOps.getItems( diff --git a/test/lib/ufe/testUIInfoHandler.py b/test/lib/ufe/testUIInfoHandler.py new file mode 100644 index 0000000000..a4802c997f --- /dev/null +++ b/test/lib/ufe/testUIInfoHandler.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# +# Copyright 2020 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 os + +import maya.cmds as cmds + +from ufeTestUtils import mayaUtils +import ufe + +import unittest + +@unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '2022', 'UIInfoHandlerTestCase is only available in Maya with UFE preview version 0.2.22 and greater') +class UIInfoHandlerTestCase(unittest.TestCase): + '''Verify the UIInfoHandler USD implementation. + ''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + def setUp(self): + ''' Called initially to set up the Maya test environment ''' + # Load plugins + self.assertTrue(self.pluginsLoaded) + + # Open ballset.ma scene in test-samples + mayaUtils.openGroupBallsScene() + + # Clear selection to start off + cmds.select(clear=True) + + def testUIInfo(self): + rid = ufe.RunTimeMgr.instance().getId('USD') + ufeUIInfo = ufe.UIInfoHandler.uiInfoHandler(rid) + self.assertIsNotNone(ufeUIInfo) + + # We won't test the actual string. Just make sure its not empty. + self.assertNotEqual(0, len(ufeUIInfo.getLongRunTimeLabel())) + + # Deactivate one of the balls. + cmds.delete('|transform1|proxyShape1,/Ball_set/Props/Ball_3') + + # Now ask for the treeViewCellInfo and test that some values were set + # (because the ball is inactive). + ball3Path = ufe.PathString.path('|transform1|proxyShape1,/Ball_set/Props/Ball_3') + ball3Hier = ufe.Hierarchy.createItem(ball3Path) + + # A return value of true means something was set. + # Note: operator= in Python creates a new variable that shares the reference + # of the original object. So don't create initTextFgClr from ci.textFgColor. + ci = ufe.CellInfo() + initTextFgClr = ufe.Color3f(ci.textFgColor.r(), ci.textFgColor.g(), ci.textFgColor.b()) + changed = ufeUIInfo.treeViewCellInfo(ball3Hier, ci) + self.assertTrue(changed) + + # Strikeout should be be set and the text fg color changed. + self.assertTrue(ci.fontStrikeout) + self.assertTrue(initTextFgClr != ci.textFgColor) From e19fa54bb6955fb94e66aef00a39385e250dd73a Mon Sep 17 00:00:00 2001 From: Sean Donnelly <23455376+seando-adsk@users.noreply.github.com> Date: Mon, 14 Sep 2020 11:33:27 -0400 Subject: [PATCH 2/2] MAYA-106188 - As a user, I would like to activate and deactivate prims * Code review comments: ** Need child filter for ProxyShapeHierarchy. ** Store Stage/SdfPath instead of UsdPrim. --- lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp | 59 ++++++++++++++----- lib/mayaUsd/ufe/ProxyShapeHierarchy.h | 1 + .../ufe/ProxyShapeHierarchyHandler.cpp | 4 +- lib/mayaUsd/ufe/UsdContextOps.cpp | 26 +++++--- lib/mayaUsd/ufe/UsdHierarchy.cpp | 2 +- lib/mayaUsd/ufe/UsdHierarchyHandler.cpp | 7 +-- test/lib/ufe/testChildFilter.py | 4 +- 7 files changed, 73 insertions(+), 30 deletions(-) diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp index eb54691fd7..d375cf7df3 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp @@ -33,6 +33,17 @@ #include #endif +namespace { + UsdPrimSiblingRange getUSDFilteredChildren(const UsdPrim& prim, const Usd_PrimFlagsPredicate pred = UsdPrimDefaultPredicate) + { + // Since the equivalent of GetChildren is + // GetFilteredChildren( UsdPrimDefaultPredicate ), + // we will use that as the initial value. + // + return prim.GetFilteredChildren(pred); + } +} + MAYAUSD_NS_DEF { namespace ufe { @@ -120,7 +131,7 @@ bool ProxyShapeHierarchy::hasChildren() const UFE_LOG("invalid root prim in ProxyShapeHierarchy::hasChildren()"); return false; } - return !rootPrim.GetChildren().empty(); + return !getUSDFilteredChildren(rootPrim).empty(); } Ufe::SceneItemList ProxyShapeHierarchy::children() const @@ -130,14 +141,43 @@ Ufe::SceneItemList ProxyShapeHierarchy::children() const if (!rootPrim.IsValid()) return Ufe::SceneItemList(); - auto usdChildren = rootPrim.GetChildren(); - auto parentPath = fItem->path(); + return createUFEChildList(getUSDFilteredChildren(rootPrim)); +} + +#ifdef UFE_V2_FEATURES_AVAILABLE +#if UFE_PREVIEW_VERSION_NUM >= 2022 +Ufe::SceneItemList ProxyShapeHierarchy::filteredChildren(const ChildFilter& childFilter) const +{ + // Return filtered children of the USD root. + const UsdPrim& rootPrim = getUsdRootPrim(); + if (!rootPrim.IsValid()) + return Ufe::SceneItemList(); + // Note: for now the only child filter flag we support is "Inactive Prims". + // See UsdHierarchyHandler::childFilter() + if ((childFilter.size() == 1) && (childFilter.front().name == "InactivePrims")) + { + // See uniqueChildName() for explanation of USD filter predicate. + Usd_PrimFlagsPredicate flags = childFilter.front().value ? UsdPrimIsDefined && !UsdPrimIsAbstract + : UsdPrimDefaultPredicate; + return createUFEChildList(getUSDFilteredChildren(rootPrim, flags)); + } + + UFE_LOG("Unknown child filter"); + return Ufe::SceneItemList(); +} +#endif +#endif + +// Return UFE child list from input USD child list. +Ufe::SceneItemList ProxyShapeHierarchy::createUFEChildList(const UsdPrimSiblingRange& range) const +{ // We must create selection items for our children. These will have as // path the path of the proxy shape, with a single path segment of a // single component appended to it. + auto parentPath = fItem->path(); Ufe::SceneItemList children; - for (const auto& child : usdChildren) + for (const auto& child : range) { children.emplace_back(UsdSceneItem::create(parentPath + Ufe::PathSegment( Ufe::PathComponent(child.GetName().GetString()), g_USDRtid, '/'), child)); @@ -145,17 +185,6 @@ Ufe::SceneItemList ProxyShapeHierarchy::children() const return children; } -#ifdef UFE_V2_FEATURES_AVAILABLE -#if UFE_PREVIEW_VERSION_NUM >= 2022 -Ufe::SceneItemList ProxyShapeHierarchy::filteredChildren(const ChildFilter& childFilter) const -{ - // Currently no child filters are supported. - assert(childFilter.empty()); - return children(); -} -#endif -#endif - Ufe::SceneItem::Ptr ProxyShapeHierarchy::parent() const { return fMayaHierarchy->parent(); diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.h b/lib/mayaUsd/ufe/ProxyShapeHierarchy.h index 53ff41f592..f11d1719d4 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.h +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.h @@ -87,6 +87,7 @@ class MAYAUSD_CORE_PUBLIC ProxyShapeHierarchy : public Ufe::Hierarchy private: const UsdPrim& getUsdRootPrim() const; + Ufe::SceneItemList createUFEChildList(const UsdPrimSiblingRange& range) const; private: Ufe::SceneItem::Ptr fItem; diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp index 71aadb2cec..ae39fc9b70 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchyHandler.cpp @@ -61,7 +61,9 @@ Ufe::SceneItem::Ptr ProxyShapeHierarchyHandler::createItem(const Ufe::Path& path #if UFE_PREVIEW_VERSION_NUM >= 2022 Ufe::Hierarchy::ChildFilter ProxyShapeHierarchyHandler::childFilter() const { - return Ufe::Hierarchy::ChildFilter(); + Ufe::Hierarchy::ChildFilter childFilters; + childFilters.emplace_back("InactivePrims", "Inactive Prims", true); + return childFilters; } #endif #endif diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index b3ba7f1b59..cb34224878 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -100,25 +100,37 @@ class ToggleActiveStateCommand : public Ufe::UndoableCommand { public: ToggleActiveStateCommand(const UsdPrim& prim) - : _prim(prim) { - _active = _prim.IsActive(); + _stage = prim.GetStage(); + _primPath = prim.GetPath(); + _active = prim.IsActive(); } void undo() override { - MayaUsd::ufe::InAddOrDeleteOperation ad; - _prim.SetActive(_active); + if (_stage) { + UsdPrim prim = _stage->GetPrimAtPath(_primPath); + if (prim.IsValid()) { + MayaUsd::ufe::InAddOrDeleteOperation ad; + prim.SetActive(_active); + } + } } void redo() override { - MayaUsd::ufe::InAddOrDeleteOperation ad; - _prim.SetActive(!_active); + if (_stage) { + UsdPrim prim = _stage->GetPrimAtPath(_primPath); + if (prim.IsValid()) { + MayaUsd::ufe::InAddOrDeleteOperation ad; + prim.SetActive(!_active); + } + } } private: - UsdPrim _prim; + PXR_NS::UsdStageWeakPtr _stage; + PXR_NS::SdfPath _primPath; bool _active; }; diff --git a/lib/mayaUsd/ufe/UsdHierarchy.cpp b/lib/mayaUsd/ufe/UsdHierarchy.cpp index c7284c3d6d..72367708c5 100644 --- a/lib/mayaUsd/ufe/UsdHierarchy.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchy.cpp @@ -131,7 +131,7 @@ Ufe::SceneItemList UsdHierarchy::createUFEChildList(const UsdPrimSiblingRange& r { // Return UFE child list from input USD child list. Ufe::SceneItemList children; - for (auto child : range) + for (const auto& child : range) { children.emplace_back(UsdSceneItem::create(fItem->path() + child.GetName(), child)); } diff --git a/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp b/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp index 80078f0ce4..019ae59c32 100644 --- a/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchyHandler.cpp @@ -56,10 +56,9 @@ Ufe::SceneItem::Ptr UsdHierarchyHandler::createItem(const Ufe::Path& path) const #if UFE_PREVIEW_VERSION_NUM >= 2022 Ufe::Hierarchy::ChildFilter UsdHierarchyHandler::childFilter() const { - Ufe::ChildFilterFlag inactivePrims("InactivePrims", "Inactive Prims", true); - Ufe::Hierarchy::ChildFilter childFilters; - childFilters.emplace_back(inactivePrims); - return childFilters; + Ufe::Hierarchy::ChildFilter childFilters; + childFilters.emplace_back("InactivePrims", "Inactive Prims", true); + return childFilters; } #endif #endif diff --git a/test/lib/ufe/testChildFilter.py b/test/lib/ufe/testChildFilter.py index 139fcbf1dc..2c22d932ba 100644 --- a/test/lib/ufe/testChildFilter.py +++ b/test/lib/ufe/testChildFilter.py @@ -75,8 +75,8 @@ def testFilteredChildren(self): # Make sure the USD hierarchy handler has an inactive prims filter self.assertEqual('InactivePrims', cf[0].name) - # Toggle "Inactive Prims" on and get the filtered chlidren with - # Inactive Prims and verify ball3 is one of them. + # Toggle "Inactive Prims" on and get the filtered children + # (with inactive prims) and verify ball3 is one of them. cf[0].value = True children = propsHier.filteredChildren(cf) self.assertEqual(6, len(children))