Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USD pick with kind support, with test. #115

Merged
merged 4 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question : The three added lines in this file are already present in the CMakeLists of mayaHydraLib/hydraExtensions, should we consider making them public there instead of duplicating them here, or do we want to keep it separate? (same goes for UFE, where we also repeat some include directories and link libraries across ufeExtensions, hydraExtensions and mayaPlugin)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it helps to keep the dependencies explicit, but I don't have a strong opinion here.

)

# -----------------------------------------------------------------------------
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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that this file is becoming a picking kitchen sink. Will think about extracting this in a later pull request.

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()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
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(
debloip-adsk marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename for consistency: these tests pick USD data.

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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The USD native instance picking C++ test code was not specific to native instancing, so renamed it for generality.

)

# -----------------------------------------------------------------------------
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"
}
}
}
}
}
}
Loading