-
Notifications
You must be signed in to change notification settings - Fork 4
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
---|---|---|
|
@@ -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> | ||
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
{ | ||
|
@@ -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( | ||
debloip-adsk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,8 +47,9 @@ set(INTERACTIVE_TEST_SCRIPT_FILES | |
cpp/testSceneCorrectness.py | ||
cpp/testPrimInstancing.py | ||
cpp/testPicking.py | ||
cpp/testPointInstancePicking.py | ||
cpp/testUsdPointInstancePicking.py | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,8 +35,8 @@ target_sources(${TARGET_NAME} | |
testSceneCorrectness.cpp | ||
testPrimInstancing.cpp | ||
testPicking.cpp | ||
testPointInstancePicking.cpp | ||
testUsdNativeInstancePicking.cpp | ||
testUsdPointInstancePicking.cpp | ||
testUsdPicking.cpp | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
) | ||
|
||
# ----------------------------------------------------------------------------- | ||
|
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 |
---|---|---|
@@ -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" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.