diff --git a/lib/mayaHydra/mayaPlugin/CMakeLists.txt b/lib/mayaHydra/mayaPlugin/CMakeLists.txt index 6dde4b1fab..67c9b74968 100644 --- a/lib/mayaHydra/mayaPlugin/CMakeLists.txt +++ b/lib/mayaHydra/mayaPlugin/CMakeLists.txt @@ -38,6 +38,7 @@ target_compile_definitions(${TARGET_NAME} $<$:LINUX> # Not sure if msvcc sets this automatically, but won't hurt to redefine $<$:_WIN32> + $<$:MAYAHYDRALIB_MAYAUSDAPI_ENABLED> ) # ----------------------------------------------------------------------------- @@ -46,6 +47,7 @@ target_compile_definitions(${TARGET_NAME} target_include_directories(${TARGET_NAME} PRIVATE $<$:${UFE_INCLUDE_DIR}> + $<$:${MAYAUSD_INCLUDE_DIR}> ) if(DEFINED MAYAUSD_VERSION) @@ -73,6 +75,7 @@ target_link_libraries(${TARGET_NAME} ufeExtensions flowViewport $<$:${UFE_LIBRARY}> + $<$:${MAYAUSDAPI_LIBRARY}> ) # ----------------------------------------------------------------------------- diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index 24dc772198..698447dd88 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -82,8 +82,13 @@ #include #include #include +#include +#include +#include #include +#include + #include #include #include @@ -164,6 +169,45 @@ PXR_NAMESPACE_USING_DIRECTIVE static const SdfPath MAYA_NATIVE_ROOT = SdfPath("/MayaHydraViewportRenderer"); +// Copy pasted from +// https://github.com/Autodesk/maya-usd/blob/dev/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp + +//! \brief Query the Kind to be selected from viewport. +//! \return A Kind token (https://graphics.pixar.com/usd/docs/api/kind_page_front.html). If the +//! token is empty or non-existing in the hierarchy, the exact prim that gets picked +//! in the viewport will be selected. +TfToken GetSelectionKind() +{ + static const MString kOptionVarName(MayaUsdPickOptionVars->SelectionKind.GetText()); + + if (MGlobal::optionVarExists(kOptionVarName)) { + MString optionVarValue = MGlobal::optionVarStringValue(kOptionVarName); + return TfToken(optionVarValue.asChar()); + } + return TfToken(); +} + +//! \brief Returns the prim or an ancestor of it that is of the given kind. +// +// If neither the prim itself nor any of its ancestors above it in the +// namespace hierarchy have an authored kind that matches, an invalid null +// prim is returned. +UsdPrim GetPrimOrAncestorWithKind(const UsdPrim& prim, const TfToken& kind) +{ + UsdPrim iterPrim = prim; + TfToken primKind; + + while (iterPrim) { + if (UsdModelAPI(iterPrim).GetKind(&primKind) && KindRegistry::IsA(primKind, kind)) { + break; + } + + iterPrim = iterPrim.GetParent(); + } + + return iterPrim; +} + //! Pick resolution behavior to use when the picked object is a point instance. enum UsdPointInstancesPickMode { @@ -287,6 +331,15 @@ HitPath pickInstancer( return {instancerPrimOrigin(primOrigin.instancerContexts.front()), -1}; } +Ufe::Path usdPathToUfePath( + const MayaHydraSceneIndexRegistrationPtr& registration, + const SdfPath& usdPath +) +{ + return registration ? registration->interpretRprimPathFn( + registration->pluginSceneIndex, usdPath) : Ufe::Path(); +} + } PXR_NAMESPACE_OPEN_SCOPE @@ -445,19 +498,48 @@ class UsdPickHandler : public MtohRenderOverride::PickHandlerBase { // For the USD pick handler pick results are directly returned with USD // scene paths, so no need to remove scene index plugin path prefix. - const auto& [pickedPath, instanceNdx] = hitPath(pickInput.pickHit); - Ufe::Path interpretedPath(registration->interpretRprimPathFn( - registration->pluginSceneIndex, pickedPath)); - - // Appending a numeric component to the path to identify a point - // instance cannot be done on the picked SdfPath, as numeric path - // components are not allowed by SdfPath. Do so here with Ufe::Path, - // which has no such restriction. - if (instanceNdx >= 0) { - interpretedPath = interpretedPath + std::to_string(instanceNdx); - } + const auto& [pickedUsdPath, instanceNdx] = hitPath(pickInput.pickHit); + + const auto pickedMayaPath = usdPathToUfePath(registration, pickedUsdPath); + const auto snMayaPath = + // As per https://stackoverflow.com/questions/46114214 + // structured bindings cannot be captured by a lambda in C++ 17, + // so pass in pickedUsdPath and instanceNdx as lambda arguments. + [&pickedMayaPath, ®istration]( + const SdfPath& pickedUsdPath, int instanceNdx) { + + if (instanceNdx >= 0) { + // Point instance: add the instance index to the path. + // Appending a numeric component to the path to identify a + // point instance cannot be done on the picked SdfPath, as + // numeric path components are not allowed by SdfPath. Do so + // here with Ufe::Path, which has no such restriction. + return pickedMayaPath + std::to_string(instanceNdx); + } + + // Not an instance: adjust picked path for selection kind. + auto snKind = GetSelectionKind(); + if (snKind.IsEmpty()) { + return pickedMayaPath; + } + + // Get the prim from the stage and path, to access the + // UsdModelAPI for the prim. + auto proxyShapeObj = registration->dagNode.object(); + if (proxyShapeObj.isNull()) { + TF_FATAL_ERROR("No mayaUsd proxy shape object corresponds to USD pick"); + return pickedMayaPath; + } + + MayaUsdAPI::ProxyStage proxyStage{proxyShapeObj}; + auto prim = proxyStage.getUsdStage()->GetPrimAtPath(pickedUsdPath); + prim = GetPrimOrAncestorWithKind(prim, snKind); + const auto usdPath = prim ? prim.GetPath() : pickedUsdPath; + + return usdPathToUfePath(registration, usdPath); + }(pickedUsdPath, instanceNdx); - auto si = Ufe::Hierarchy::createItem(interpretedPath); + auto si = Ufe::Hierarchy::createItem(snMayaPath); if (!si) { return false; } diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index afe9fcc93f..61b925c694 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -47,8 +47,9 @@ set(INTERACTIVE_TEST_SCRIPT_FILES cpp/testSceneCorrectness.py cpp/testPrimInstancing.py cpp/testPicking.py - cpp/testPointInstancePicking.py + cpp/testUsdPointInstancePicking.py cpp/testUsdNativeInstancePicking.py + cpp/testUsdPickKind.py ) #Add this test only if the MayaUsd_FOUND (so also MAYAUSDAPI_LIBRARY) has been found during compile time. diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt index e5a1720083..738b27ff11 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt @@ -35,8 +35,8 @@ target_sources(${TARGET_NAME} testSceneCorrectness.cpp testPrimInstancing.cpp testPicking.cpp - testPointInstancePicking.cpp - testUsdNativeInstancePicking.cpp + testUsdPointInstancePicking.cpp + testUsdPicking.cpp ) # ----------------------------------------------------------------------------- diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.py index 77eb979385..2c6a4ac94d 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.py +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.py @@ -41,7 +41,7 @@ def test_NativeInstances(self): for instance in instances: cmds.mayaHydraCppTest( self.PICK_PATH + instance, - f="TestUsdNativeInstancePicking.pickInstance") + f="TestUsdPicking.pick") if __name__ == '__main__': fixturesUtils.runTests(globals()) diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPickKind.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPickKind.py new file mode 100644 index 0000000000..d3a2993da8 --- /dev/null +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPickKind.py @@ -0,0 +1,73 @@ +# 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. +# 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 usdUtils + +import testUtils +from testUtils import PluginLoaded + +class TestUsdPickKind(mtohUtils.MayaHydraBaseTestCase): + # MayaHydraBaseTestCase.setUpClass requirement. + _file = __file__ + + PICK_PATH = "|kindHierarchy|kindHierarchyShape,/RootAssembly/ParentGroup" + + def loadUsdScene(self): + usdScenePath = testUtils.getTestScene('testUsdPickKind', 'kindHierarchy.usda') + usdUtils.createStageFromFile(usdScenePath) + + def setUp(self): + super(TestUsdPickKind, self).setUp() + self.loadUsdScene() + cmds.refresh() + + def test_pickKinds(self): + with PluginLoaded('mayaHydraCppTests'): + kinds = ["", "model", "group", "assembly", "component", "subcomponent"] + selectedItems = [ + # Kind is none: pick the most descendant prim. + "/ChildAssembly/LeafModel/ImportantSubtree/Cube", + # Kind is model: subcomponent is not part of the model kind + # hierarchy, so we iterate up the parent hierarchy twice to + # reach the component prim. + "/ChildAssembly/LeafModel", + # Kind is group: an assembly is a group, so ChildAssembly is + # picked. + "/ChildAssembly", + # Kind is assembly. + "/ChildAssembly", + # Kind is component + "/ChildAssembly/LeafModel", + # Kind is subcomponent + "/ChildAssembly/LeafModel/ImportantSubtree" + ] + + # Read the current USD selection kind. + kindOptionVar = "mayaUsd_SelectionKind" + previousKind = cmds.optionVar(q=kindOptionVar) + + for (kind, selectedItem) in zip(kinds, selectedItems): + cmds.optionVar(sv=(kindOptionVar, kind)) + cmds.mayaHydraCppTest( + self.PICK_PATH + selectedItem, + f="TestUsdPicking.pick") + + # Restore the USD selection kind back to its original value. + cmds.optionVar(sv=(kindOptionVar, previousKind)) + +if __name__ == '__main__': + fixturesUtils.runTests(globals()) diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPicking.cpp similarity index 98% rename from test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.cpp rename to test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPicking.cpp index ccf1197555..95b5afe00e 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdNativeInstancePicking.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPicking.cpp @@ -32,7 +32,7 @@ PXR_NAMESPACE_USING_DIRECTIVE using namespace MayaHydra; -TEST(TestUsdNativeInstancePicking, pickInstance) +TEST(TestUsdPicking, pick) { const auto& sceneIndices = GetTerminalSceneIndices(); ASSERT_GT(sceneIndices.size(), 0u); diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPointInstancePicking.cpp b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPointInstancePicking.cpp similarity index 97% rename from test/lib/mayaUsd/render/mayaToHydra/cpp/testPointInstancePicking.cpp rename to test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPointInstancePicking.cpp index 6c9a60d824..a153d20476 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPointInstancePicking.cpp +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPointInstancePicking.cpp @@ -29,7 +29,7 @@ PXR_NAMESPACE_USING_DIRECTIVE using namespace MayaHydra; -TEST(TestPointInstancePicking, pickPointInstance) +TEST(TestUsdPointInstancePicking, pickPointInstance) { const auto& sceneIndices = GetTerminalSceneIndices(); ASSERT_GT(sceneIndices.size(), 0u); diff --git a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPointInstancePicking.py b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPointInstancePicking.py similarity index 91% rename from test/lib/mayaUsd/render/mayaToHydra/cpp/testPointInstancePicking.py rename to test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPointInstancePicking.py index 7021cc6a11..6c71d965fd 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/cpp/testPointInstancePicking.py +++ b/test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPointInstancePicking.py @@ -20,7 +20,7 @@ import testUtils from testUtils import PluginLoaded -class TestPointInstancePicking(mtohUtils.MayaHydraBaseTestCase): +class TestUsdPointInstancePicking(mtohUtils.MayaHydraBaseTestCase): # MayaHydraBaseTestCase.setUpClass requirement. _file = __file__ @@ -34,11 +34,11 @@ class TestPointInstancePicking(mtohUtils.MayaHydraBaseTestCase): # we want to pick, and use the marker objects to determine the projected # mouse coordinates. def loadUsdScene(self): - usdScenePath = testUtils.getTestScene('testPointInstances', 'nestedPointInstancers.usda') + usdScenePath = testUtils.getTestScene('testUsdPointInstances', 'nestedPointInstancers.usda') usdUtils.createStageFromFile(usdScenePath) def setUp(self): - super(TestPointInstancePicking, self).setUp() + super(TestUsdPointInstancePicking, self).setUp() self.loadUsdScene() cmds.setAttr('persp.translate', 19.3, 13.7, 11.4, type='float3') cmds.setAttr('persp.rotate', -33.4, 63.0, 0, type='float3') @@ -59,7 +59,7 @@ def test_PickPointInstancer(self): cmds.mayaHydraCppTest( self.PICK_PATH + "/ParentPointInstancer", self.PICK_PATH + marker, - f="TestPointInstancePicking.pickPointInstance") + f="TestUsdPointInstancePicking.pickPointInstance") def test_PickInstances(self): with PluginLoaded('mayaHydraCppTests'): @@ -82,7 +82,7 @@ def test_PickInstances(self): cmds.mayaHydraCppTest( self.PICK_PATH + instance, self.PICK_PATH + marker, - f="TestPointInstancePicking.pickPointInstance") + f="TestUsdPointInstancePicking.pickPointInstance") def test_PickPrototypes(self): with PluginLoaded('mayaHydraCppTests'): @@ -106,7 +106,7 @@ def test_PickPrototypes(self): cmds.mayaHydraCppTest( self.PICK_PATH + prototype, self.PICK_PATH + marker, - f="TestPointInstancePicking.pickPointInstance") + f="TestUsdPointInstancePicking.pickPointInstance") if __name__ == '__main__': fixturesUtils.runTests(globals()) diff --git a/test/testSamples/testUsdPickKind/kindHierarchy.usda b/test/testSamples/testUsdPickKind/kindHierarchy.usda new file mode 100644 index 0000000000..2cb7d9b4cf --- /dev/null +++ b/test/testSamples/testUsdPickKind/kindHierarchy.usda @@ -0,0 +1,35 @@ +#usda 1.0 + +def Xform "RootAssembly" ( + kind = "assembly" +) +{ + def Xform "ParentGroup" ( + kind = "group" + ) + { + def Xform "ChildAssembly" ( + kind = "assembly" + ) + { + def Xform "LeafModel" ( + kind = "component" + ) + { + def Xform "ImportantSubtree" ( + kind = "subcomponent" + ) + { + def Mesh "Cube" + { + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] + int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4] + point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)] + uniform token subdivisionScheme = "none" + } + } + } + } + } +} diff --git a/test/testSamples/testPointInstances/CubeModel.usda b/test/testSamples/testUsdPointInstances/CubeModel.usda similarity index 100% rename from test/testSamples/testPointInstances/CubeModel.usda rename to test/testSamples/testUsdPointInstances/CubeModel.usda diff --git a/test/testSamples/testPointInstances/PyramidModel.usda b/test/testSamples/testUsdPointInstances/PyramidModel.usda similarity index 100% rename from test/testSamples/testPointInstances/PyramidModel.usda rename to test/testSamples/testUsdPointInstances/PyramidModel.usda diff --git a/test/testSamples/testPointInstances/nestedPointInstancers.usda b/test/testSamples/testUsdPointInstances/nestedPointInstancers.usda similarity index 100% rename from test/testSamples/testPointInstances/nestedPointInstancers.usda rename to test/testSamples/testUsdPointInstances/nestedPointInstancers.usda