Skip to content

Commit

Permalink
USD pick with kind support, with test.
Browse files Browse the repository at this point in the history
  • Loading branch information
ppt-adsk committed Apr 3, 2024
1 parent b5e22e1 commit 6b1661c
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 24 deletions.
3 changes: 3 additions & 0 deletions lib/mayaHydra/mayaPlugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ target_compile_definitions(${TARGET_NAME}
$<$<BOOL:${IS_LINUX}>:LINUX>
# Not sure if msvcc sets this automatically, but won't hurt to redefine
$<$<BOOL:${IS_WINDOWS}>:_WIN32>
$<$<BOOL:${MayaUsd_FOUND}>:MAYAHYDRALIB_MAYAUSDAPI_ENABLED>
)

# -----------------------------------------------------------------------------
Expand All @@ -46,6 +47,7 @@ target_compile_definitions(${TARGET_NAME}
target_include_directories(${TARGET_NAME}
PRIVATE
$<$<BOOL:${UFE_FOUND}>:${UFE_INCLUDE_DIR}>
$<$<BOOL:${MayaUsd_FOUND}>:${MAYAUSD_INCLUDE_DIR}>
)

if(DEFINED MAYAUSD_VERSION)
Expand Down Expand Up @@ -73,6 +75,7 @@ target_link_libraries(${TARGET_NAME}
ufeExtensions
flowViewport
$<$<BOOL:${UFE_FOUND}>:${UFE_LIBRARY}>
$<$<BOOL:${MayaUsd_FOUND}>:${MAYAUSDAPI_LIBRARY}>
)

# -----------------------------------------------------------------------------
Expand Down
97 changes: 85 additions & 12 deletions lib/mayaHydra/mayaPlugin/renderOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,13 @@
#include <pxr/imaging/hdx/tokens.h>
#include <pxr/imaging/hgi/hgi.h>
#include <pxr/imaging/hgi/tokens.h>
#include <pxr/usd/kind/registry.h>
#include <pxr/usd/usd/prim.h>
#include <pxr/usd/usd/modelAPI.h>
#include <pxr/pxr.h>

#include <mayaUsdAPI/proxyStage.h>

#include <maya/M3dView.h>
#include <maya/MConditionMessage.h>
#include <maya/MDGMessage.h>
Expand Down Expand Up @@ -164,6 +169,42 @@ PXR_NAMESPACE_USING_DIRECTIVE

static const SdfPath MAYA_NATIVE_ROOT = SdfPath("/MayaHydraViewportRenderer");

//! \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
{
Expand Down Expand Up @@ -287,6 +328,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
Expand Down Expand Up @@ -445,19 +495,42 @@ 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 = (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.
(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);
}();

auto si = Ufe::Hierarchy::createItem(interpretedPath);
auto si = Ufe::Hierarchy::createItem(snMayaPath);
if (!si) {
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions test/lib/mayaUsd/render/mayaToHydra/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ target_sources(${TARGET_NAME}
testSceneCorrectness.cpp
testPrimInstancing.cpp
testPicking.cpp
testPointInstancePicking.cpp
testUsdNativeInstancePicking.cpp
testUsdPointInstancePicking.cpp
testUsdPicking.cpp
)

# -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
73 changes: 73 additions & 0 deletions test/lib/mayaUsd/render/mayaToHydra/cpp/testUsdPickKind.py
Original file line number Diff line number Diff line change
@@ -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())
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import testUtils
from testUtils import PluginLoaded

class TestPointInstancePicking(mtohUtils.MayaHydraBaseTestCase):
class TestUsdPointInstancePicking(mtohUtils.MayaHydraBaseTestCase):
# MayaHydraBaseTestCase.setUpClass requirement.
_file = __file__

Expand All @@ -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')
Expand All @@ -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'):
Expand All @@ -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'):
Expand All @@ -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())
35 changes: 35 additions & 0 deletions test/testSamples/testUsdPickKind/kindHierarchy.usda
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
}

0 comments on commit 6b1661c

Please sign in to comment.