From 8a2838421705b5b2895a9af89cc98625c419ecea Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Thu, 20 Jun 2024 16:17:51 +0200 Subject: [PATCH 1/7] HYDRA-30 : support faces picking in Hydra viewport --- ...pWireframeSelectionHighlightSceneIndex.cpp | 3 + .../adapters/renderItemAdapter.cpp | 32 ++- .../mhWireframeColorInterfaceImp.cpp | 9 +- lib/mayaHydra/hydraExtensions/mixedUtils.cpp | 9 + lib/mayaHydra/hydraExtensions/mixedUtils.h | 9 + .../hydraExtensions/sceneIndex/CMakeLists.txt | 4 +- ...ce.cpp => mayaHydraMaterialDataSource.cpp} | 12 +- ...Source.h => mayaHydraMaterialDataSource.h} | 16 +- .../sceneIndex/mayaHydraSceneIndex.cpp | 60 ++++- .../sceneIndex/mayaHydraSceneIndex.h | 8 +- lib/mayaHydra/mayaPlugin/renderOverride.cpp | 16 ++ .../mayaUsd/render/mayaToHydra/CMakeLists.txt | 1 + .../face236SelStorm.png | Bin 0 -> 10179 bytes .../facesSelStorm.png | Bin 0 -> 16922 bytes .../MayaComponentsPickingTest/facesSelVP2.png | Bin 0 -> 16922 bytes .../smoothwireframe.png | Bin 0 -> 18127 bytes .../mayaToHydra/testMayaComponentsPicking.py | 57 +++++ .../testMayaComponentsPicking.ma | 228 ++++++++++++++++++ 18 files changed, 435 insertions(+), 29 deletions(-) rename lib/mayaHydra/hydraExtensions/sceneIndex/{mayaHydraDefaultMaterialDataSource.cpp => mayaHydraMaterialDataSource.cpp} (89%) rename lib/mayaHydra/hydraExtensions/sceneIndex/{mayaHydraDefaultMaterialDataSource.h => mayaHydraMaterialDataSource.h} (74%) create mode 100644 test/lib/mayaUsd/render/mayaToHydra/MayaComponentsPickingTest/face236SelStorm.png create mode 100644 test/lib/mayaUsd/render/mayaToHydra/MayaComponentsPickingTest/facesSelStorm.png create mode 100644 test/lib/mayaUsd/render/mayaToHydra/MayaComponentsPickingTest/facesSelVP2.png create mode 100644 test/lib/mayaUsd/render/mayaToHydra/MayaComponentsPickingTest/smoothwireframe.png create mode 100644 test/lib/mayaUsd/render/mayaToHydra/testMayaComponentsPicking.py create mode 100644 test/testSamples/testMayaComponentsPicking/testMayaComponentsPicking.ma diff --git a/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp b/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp index f87a0fcf1f..9b8fa82bdb 100644 --- a/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp +++ b/lib/flowViewport/sceneIndex/fvpWireframeSelectionHighlightSceneIndex.cpp @@ -70,6 +70,9 @@ const std::string selectionHighlightMirrorTag = "_SelectionHighlight"; SdfPath _GetSelectionHighlightMirrorPathFromOriginal(const SdfPath& originalPath) { + if (originalPath == SdfPath::AbsoluteRootPath()) { + return originalPath; //Avoid a warning in Hydra + } return originalPath.ReplaceName(TfToken(originalPath.GetName() + selectionHighlightMirrorTag)); } diff --git a/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp b/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp index 0dddc8b718..4e2ad6d9ed 100644 --- a/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp +++ b/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp @@ -142,7 +142,7 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data // const bool isNew = flags & MViewportScene::MVS_new; //not used yet const bool visible = data._flags & MVS::MVS_visible; const bool matrixChanged = data._flags & MVS::MVS_changedMatrix; - const bool geomChanged = (data._flags & MVS::MVS_changedGeometry) || positionsHaveBeenReset; + bool geomChanged = (data._flags & MVS::MVS_changedGeometry) || positionsHaveBeenReset;//Non const as we may modify it later const bool topoChanged = (data._flags & MVS::MVS_changedTopo) || positionsHaveBeenReset; const bool visibChanged = data._flags & MVS::MVS_changedVisibility; const bool effectChanged = data._flags & MVS::MVS_changedEffect; @@ -192,6 +192,34 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data static const bool passNormalsToHydra = MayaHydraSceneIndex::passNormalsToHydra(); const int vertexBuffercount = geom ? geom->vertexBufferCount() : 0; + + //Temp workaround for a bug in Maya MAYA-134200 + if ((!geomChanged && topoChanged) && vertexBuffercount) { + //With face components selection, we have topoChanged which is true but geomChanged is false, but this is wrong, the number of vertices may have changed. + //We want to check here if we also need to update the geometry if the number of vertices is different from what is stored already + for (int vbIdx = 0; vbIdx < vertexBuffercount; vbIdx++) { + MVertexBuffer* mvb = geom->vertexBuffer(vbIdx); + if (!mvb) { + continue; + } + + const MVertexBufferDescriptor& desc = mvb->descriptor(); + const auto semantic = desc.semantic(); + switch (semantic) { + case MGeometry::Semantic::kPosition: { + // Vertices + MVertexBuffer* verts = mvb; + const unsigned int originalVertexCount = verts->vertexCount(); + if (_positions.size() != originalVertexCount) {//Is it different ? + geomChanged = true; + vbIdx = vertexBuffercount; //Stop looping + } + } break; + default: break; + } + } + } + // Vertices if (geomChanged && vertexBuffercount) { //vertexBuffercount > 0 means geom is non null @@ -352,7 +380,7 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data } } - if (topoChanged) { + if (topoChanged && (vertexCounts.size())) { switch (GetPrimitive()) { case MGeometry::Primitive::kTriangles:{ static const bool passNormalsToHydra = MayaHydraSceneIndex::passNormalsToHydra(); diff --git a/lib/mayaHydra/hydraExtensions/mhWireframeColorInterfaceImp.cpp b/lib/mayaHydra/hydraExtensions/mhWireframeColorInterfaceImp.cpp index d2064af9f4..8852e74875 100644 --- a/lib/mayaHydra/hydraExtensions/mhWireframeColorInterfaceImp.cpp +++ b/lib/mayaHydra/hydraExtensions/mhWireframeColorInterfaceImp.cpp @@ -16,6 +16,7 @@ //Local headers #include "mhWireframeColorInterfaceImp.h" +#include "mixedUtils.h" //Flow viewport headers #include @@ -29,14 +30,6 @@ PXR_NAMESPACE_USING_DIRECTIVE -// An implementation for maya of the WireframeColorInterface to get the wireframe color from a prim for selection highlighting -namespace { - PXR_NS::GfVec4f getPreferencesColor(const PXR_NS::TfToken& token) { - PXR_NS::GfVec4f color; - Fvp::ColorPreferences::getInstance().getColor(token, color); - return color; - } -} namespace MAYAHYDRA_NS_DEF { MhWireframeColorInterfaceImp::MhWireframeColorInterfaceImp(const std::shared_ptr& selection diff --git a/lib/mayaHydra/hydraExtensions/mixedUtils.cpp b/lib/mayaHydra/hydraExtensions/mixedUtils.cpp index b796aaa46a..98f638faaa 100644 --- a/lib/mayaHydra/hydraExtensions/mixedUtils.cpp +++ b/lib/mayaHydra/hydraExtensions/mixedUtils.cpp @@ -17,6 +17,8 @@ #include "mixedUtils.h" +#include + #include #include @@ -172,4 +174,11 @@ bool getIndexedColorPreferenceValue( return false; } +PXR_NS::GfVec4f getPreferencesColor(const PXR_NS::TfToken& token) +{ + PXR_NS::GfVec4f color; + Fvp::ColorPreferences::getInstance().getColor(token, color); + return color; +} + } // namespace MAYAHYDRA_NS_DEF diff --git a/lib/mayaHydra/hydraExtensions/mixedUtils.h b/lib/mayaHydra/hydraExtensions/mixedUtils.h index a35e1d3c08..21f2954313 100644 --- a/lib/mayaHydra/hydraExtensions/mixedUtils.h +++ b/lib/mayaHydra/hydraExtensions/mixedUtils.h @@ -225,6 +225,15 @@ bool getIndexedColorPreferenceValue( const std::string& tableName, PXR_NS::GfVec4f& outColor); +/** + * @brief Retrieves a color preference from Maya using the Flow Viewport Color Preferences API. + * + * @param[in] token is a Flow Viewport color preferences token e.g : FvpColorPreferencesTokens->polymeshDormant + * + * @return the color that will be populated if retrieved from Maya. + */ +PXR_NS::GfVec4f getPreferencesColor(const PXR_NS::TfToken& token); + } // namespace MAYAHYDRA_NS_DEF #endif // MAYAHYDRALIB_MIXED_UTILS_H diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/CMakeLists.txt b/lib/mayaHydra/hydraExtensions/sceneIndex/CMakeLists.txt index 5f09979edb..976ab5d11e 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/CMakeLists.txt +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/CMakeLists.txt @@ -11,7 +11,7 @@ target_sources(${TARGET_NAME} mayaHydraCameraDataSource.cpp mayaHydraLightDataSource.cpp mayaHydraDefaultLightDataSource.cpp - mayaHydraDefaultMaterialDataSource.cpp + mayaHydraMaterialDataSource.cpp mayaHydraMayaDataProducerSceneIndexData.cpp mayaHydraMayaDataProducerSceneIndexDataConcreteFactory.cpp mayaHydraSceneIndexDataFactoriesSetup.cpp @@ -31,7 +31,7 @@ set(HEADERS mayaHydraLightDataSource.h mayaHydraSceneIndexUtils.h mayaHydraDefaultLightDataSource.h - mayaHydraDefaultMaterialDataSource.h + mayaHydraMaterialDataSource.h mayaHydraMayaDataProducerSceneIndexData.h mayaHydraMayaDataProducerSceneIndexDataConcreteFactory.h mayaHydraSceneIndexDataFactoriesSetup.h diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraDefaultMaterialDataSource.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMaterialDataSource.cpp similarity index 89% rename from lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraDefaultMaterialDataSource.cpp rename to lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMaterialDataSource.cpp index cc81294184..efa27abb0a 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraDefaultMaterialDataSource.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMaterialDataSource.cpp @@ -14,7 +14,7 @@ // limitations under the License. // -#include "mayaHydraDefaultMaterialDataSource.h" +#include "mayaHydraMaterialDataSource.h" #include #include @@ -32,7 +32,7 @@ PXR_NAMESPACE_OPEN_SCOPE // ---------------------------------------------------------------------------- -MayaHydraDefaultMaterialDataSource::MayaHydraDefaultMaterialDataSource( +MayaHydraMaterialDataSource::MayaHydraMaterialDataSource( const SdfPath& id, TfToken type, MayaHydraSceneIndex* sceneIndex) @@ -44,7 +44,7 @@ MayaHydraDefaultMaterialDataSource::MayaHydraDefaultMaterialDataSource( TfTokenVector -MayaHydraDefaultMaterialDataSource::GetNames() +MayaHydraMaterialDataSource::GetNames() { TfTokenVector result; result.push_back(HdMaterialSchemaTokens->material); @@ -52,7 +52,7 @@ MayaHydraDefaultMaterialDataSource::GetNames() } HdDataSourceBaseHandle -MayaHydraDefaultMaterialDataSource::Get(const TfToken& name) +MayaHydraMaterialDataSource::Get(const TfToken& name) { if (name == HdMaterialSchemaTokens->material) { return _GetMaterialDataSource(); @@ -66,7 +66,7 @@ MayaHydraDefaultMaterialDataSource::Get(const TfToken& name) } HdDataSourceBaseHandle -MayaHydraDefaultMaterialDataSource::_GetMaterialBindingDataSource() +MayaHydraMaterialDataSource::_GetMaterialBindingDataSource() { const SdfPath path = _sceneIndex->GetMaterialId(_id); if (path.IsEmpty()) { @@ -88,7 +88,7 @@ MayaHydraDefaultMaterialDataSource::_GetMaterialBindingDataSource() } HdDataSourceBaseHandle -MayaHydraDefaultMaterialDataSource::_GetMaterialDataSource() +MayaHydraMaterialDataSource::_GetMaterialDataSource() { VtValue materialContainer = _sceneIndex->GetMaterialResource(_id); diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraDefaultMaterialDataSource.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMaterialDataSource.h similarity index 74% rename from lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraDefaultMaterialDataSource.h rename to lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMaterialDataSource.h index 5ff81ba6a0..b368cda402 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraDefaultMaterialDataSource.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraMaterialDataSource.h @@ -14,8 +14,8 @@ // limitations under the License. // -#ifndef MAYAHYDRADEFAULTMATERIALDATASOURCE_H -#define MAYAHYDRADEFAULTMATERIALDATASOURCE_H +#ifndef SCENEINDEX_MAYA_HYDRA_MATERIAL_DATASOURCE_H +#define SCENEINDEX_MAYA_HYDRA_MATERIAL_DATASOURCE_H #include #include @@ -27,12 +27,12 @@ PXR_NAMESPACE_OPEN_SCOPE class MayaHydraSceneIndex; /** - * \brief A container data source representing a default material with USDPreviewSurface + * \brief A generic container data source representing a maya hydra material */ - class MayaHydraDefaultMaterialDataSource : public HdContainerDataSource + class MayaHydraMaterialDataSource : public HdContainerDataSource { public: - HD_DECLARE_DATASOURCE(MayaHydraDefaultMaterialDataSource); + HD_DECLARE_DATASOURCE(MayaHydraMaterialDataSource); // ------------------------------------------------------------------------ // HdContainerDataSource implementations @@ -40,7 +40,7 @@ class MayaHydraSceneIndex; HdDataSourceBaseHandle Get(const TfToken& name) override; private: - MayaHydraDefaultMaterialDataSource( + MayaHydraMaterialDataSource( const SdfPath& id, TfToken type, MayaHydraSceneIndex* sceneIndex); @@ -53,8 +53,8 @@ class MayaHydraSceneIndex; MayaHydraSceneIndex* _sceneIndex = nullptr; }; -HD_DECLARE_DATASOURCE_HANDLES(MayaHydraDefaultMaterialDataSource); +HD_DECLARE_DATASOURCE_HANDLES(MayaHydraMaterialDataSource); PXR_NAMESPACE_CLOSE_SCOPE -#endif // MAYAHYDRADEFAULTMATERIALDATASOURCE_H +#endif // SCENEINDEX_MAYA_HYDRA_MATERIAL_DATASOURCE_H diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp index d14ace33fc..1300d9f17c 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp @@ -16,6 +16,8 @@ #include "mayaHydraSceneIndex.h" +#include + #include #include #include @@ -60,6 +62,7 @@ namespace { static std::mutex _adaptersToRecreateMutex; static std::mutex _adaptersToRebuildMutex; + static const MString sActiveFacesName(L"PolyActiveFaces");//When we have a render item which is a selection of faces, it always has this name in maya. } PXR_NAMESPACE_OPEN_SCOPE @@ -374,6 +377,7 @@ TF_DEFINE_PRIVATE_TOKENS( _tokens, ((MayaDefaultMaterial, "__maya_default_material__")) + ((MayaFacesSelectionMaterial, "__maya_faces_selection_material__")) (diffuseColor) (emissiveColor) (opacity) @@ -387,6 +391,8 @@ SdfPath MayaHydraSceneIndex::_fallbackMaterial; SdfPath MayaHydraSceneIndex::_mayaDefaultMaterialPath; // Common to all scene indexes VtValue MayaHydraSceneIndex::_mayaDefaultMaterial; SdfPath MayaHydraSceneIndex::_mayaDefaultLightPath; // Common to all scene indexes +SdfPath MayaHydraSceneIndex::_mayaFacesSelectionMaterialPath; // Common to all scene indexes +VtValue MayaHydraSceneIndex::_mayaFacesSelectionMaterial; MayaHydraSceneIndex::MayaHydraSceneIndex( MayaHydraInitData& initData, @@ -405,8 +411,19 @@ MayaHydraSceneIndex::MayaHydraSceneIndex( _tokens->MayaDefaultMaterial); // Is an absolute path, not linked to a scene index _mayaDefaultLightPath = SdfPath::AbsoluteRootPath().AppendChild(_tokens->DefaultMayaLight); _mayaDefaultMaterial = MayaHydraSceneIndex::CreateMayaDefaultMaterial(); + _mayaFacesSelectionMaterialPath = SdfPath::AbsoluteRootPath().AppendChild( + _tokens + ->MayaFacesSelectionMaterial); // Is an absolute path, not linked to a scene index + _mayaFacesSelectionMaterial = MayaHydraSceneIndex::CreateMayaFacesSelectionMaterial(); _fallbackMaterial = SdfPath::EmptyPath(); // Empty path for hydra fallback material }); + + //Always add the mayaHydraFacesSelectionMaterialDataSource to display faces selection + auto mayaHydraFacesSelectionMaterialDataSource = MayaHydraMaterialDataSource::New( + _mayaFacesSelectionMaterialPath, HdPrimTypeTokens->material, this); + AddPrims({ { _mayaFacesSelectionMaterialPath, + HdPrimTypeTokens->material, + mayaHydraFacesSelectionMaterialDataSource } }); } MayaHydraSceneIndex::~MayaHydraSceneIndex() @@ -418,6 +435,8 @@ MayaHydraSceneIndex::~MayaHydraSceneIndex() void MayaHydraSceneIndex::RemoveCallbacksAndDeleteAdapters() { + RemovePrims({ { _mayaFacesSelectionMaterialPath} }); //Remove _mayaFacesSelectionMaterialPath used to display faces selection + for (auto callback : _callbacks) { MMessage::removeCallback(callback); } @@ -627,6 +646,10 @@ VtValue MayaHydraSceneIndex::GetMaterialResource(const SdfPath& id) return _mayaDefaultMaterial; } + if (id == _mayaFacesSelectionMaterialPath) { + return _mayaFacesSelectionMaterial; + } + if (id == _fallbackMaterial) { return MayaHydraMaterialAdapter::GetPreviewMaterialResource(id); } @@ -659,6 +682,25 @@ VtValue MayaHydraSceneIndex::CreateMayaDefaultMaterial() return VtValue(networkMap); } +VtValue MayaHydraSceneIndex::CreateMayaFacesSelectionMaterial() +{ + const GfVec4f faceSelectioncolor = getPreferencesColor(FvpColorPreferencesTokens->faceSelection); + + HdMaterialNetworkMap networkMap; + HdMaterialNetwork network; + HdMaterialNode node; + node.identifier = UsdImagingTokens->UsdPreviewSurface; + node.path = _mayaFacesSelectionMaterialPath; + node.parameters.insert( + { _tokens->diffuseColor, + VtValue(GfVec3f(faceSelectioncolor[0], faceSelectioncolor[1], faceSelectioncolor[2])) }); + node.parameters.insert({ _tokens->opacity, VtValue(float(0.3f)) }); + network.nodes.push_back(std::move(node)); + networkMap.map.insert({ HdMaterialTerminalTokens->surface, std::move(network) }); + networkMap.terminals.push_back(_mayaFacesSelectionMaterialPath); + return VtValue(networkMap); +} + Fvp::PrimSelections MayaHydraSceneIndex::UfePathToPrimSelections(const Ufe::Path& appPath) const { TF_DEBUG(MAYAHYDRALIB_SCENE_INDEX) @@ -752,8 +794,10 @@ LightDagPathMap MayaHydraSceneIndex::_GetGlobalLightPaths() const void MayaHydraSceneIndex::SetDefaultMaterial(bool useDefMaterial) { if (useDefMaterial) { - auto mayaDefaultMaterialDataSource = MayaHydraDefaultMaterialDataSource::New(_mayaDefaultMaterialPath, HdPrimTypeTokens->material, this); - AddPrims({ { _mayaDefaultMaterialPath, HdPrimTypeTokens->material, mayaDefaultMaterialDataSource } }); + auto mayaHydraDefaultMaterialDataSource = MayaHydraMaterialDataSource::New(_mayaDefaultMaterialPath, HdPrimTypeTokens->material, this); + AddPrims({ { _mayaDefaultMaterialPath, + HdPrimTypeTokens->material, + mayaHydraDefaultMaterialDataSource } }); } else RemovePrim(_mayaDefaultMaterialPath); @@ -1127,6 +1171,10 @@ SdfPath MayaHydraSceneIndex::GetMaterialId(const SdfPath& id) return _fallbackMaterial; } + if (material == _mayaFacesSelectionMaterialPath) { + return _mayaFacesSelectionMaterialPath; + } + if (TfMapLookupPtr(_materialAdapters, material) != nullptr) { return material; } @@ -1239,6 +1287,14 @@ bool MayaHydraSceneIndex::_GetRenderItemMaterial( return true; } + //Is it a face components selection render item ? + const MString& renderItemName = ri.name(); + //Compare its name with the content of sActiveFacesName which is the hardcoded name for face components selection render item + if (renderItemName.indexW(sActiveFacesName) >= 0) { + material = _mayaFacesSelectionMaterialPath; + return true; + } + if (GetShadingEngineNode(ri, shadingEngineNode)) // Else try to find associated material node if this is a material shader. // NOTE: The existing maya material support in hydra expects a shading engine node diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h index 01a71e20e7..c0fe6fd484 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "flowViewport/sceneIndex/fvpPathInterface.h" @@ -277,6 +277,7 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public using LightDagPathMap = std::unordered_map; LightDagPathMap _GetGlobalLightPaths() const; static VtValue CreateMayaDefaultMaterial(); + static VtValue CreateMayaFacesSelectionMaterial(); using DirtyBitsToLocatorsFunc = std::function; void _MarkPrimDirty( @@ -325,6 +326,11 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public /// _useDefaultMaterial is true static VtValue _mayaDefaultMaterial; + /// _mayaFacesSelectionMaterialPath is a path to a Hydra material used to display the faces selection on nodes when being in components selection mode + static SdfPath _mayaFacesSelectionMaterialPath; + /// _mayaFacesSelectionMaterial is a Hydra material used to display the faces selection on nodes when being in components selection mode + static VtValue _mayaFacesSelectionMaterial; + // Default light GlfSimpleLight _mayaDefaultLight; bool _useMayaDefaultLight = false; diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index 144916ac48..de60dd5cae 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -345,6 +345,17 @@ inline bool areDifferentForOneOfTheseBits(unsigned int val1, unsigned int val2, return ((val1 & bitsToTest) != (val2 & bitsToTest)); } +inline bool isInComponentsPickingMode(const MHWRender::MSelectionInfo& selectInfo) +{ + return selectInfo.selectable(MSelectionMask::kSelectMeshVerts) + || selectInfo.selectable(MSelectionMask::kSelectMeshEdges) + || selectInfo.selectable(MSelectionMask::kSelectMeshFreeEdges) + || selectInfo.selectable(MSelectionMask::kSelectMeshFaces) + || selectInfo.selectable(MSelectionMask::kSelectVertices) + || selectInfo.selectable(MSelectionMask::kSelectEdges) + || selectInfo.selectable(MSelectionMask::kSelectFacets); +} + } PXR_NAMESPACE_OPEN_SCOPE @@ -1791,6 +1802,11 @@ bool MtohRenderOverride::select( "MtohRenderOverride::select", "MtohRenderOverride::select"); #endif + + if (isInComponentsPickingMode(selectInfo)) { + return false; //When being in components picking, returning false will use maya/OGS for components selection + } + MStatus status = MStatus::kFailure; MMatrix viewMatrix = frameContext.getMatrix(MHWRender::MFrameContext::kViewMtx, &status); diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index 05c32f14c3..f5009722e6 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -46,6 +46,7 @@ set(INTERACTIVE_TEST_SCRIPT_FILES testDataProducerSelHighlight.py|skipOnPlatform:osx testPassingNormalsOnMayaNative.py testViewportFilters.py + testMayaComponentsPicking.py cpp/testColorPreferences.py cpp/testCppFramework.py cpp/testMayaSceneFlattening.py diff --git a/test/lib/mayaUsd/render/mayaToHydra/MayaComponentsPickingTest/face236SelStorm.png b/test/lib/mayaUsd/render/mayaToHydra/MayaComponentsPickingTest/face236SelStorm.png new file mode 100644 index 0000000000000000000000000000000000000000..d3c5104805f491538c0420f2a2b187763945f2ae GIT binary patch literal 10179 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

9oilqUNmeHO`hkKQr|DHc9Ip_=&Q?ReoyUxdbr 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

9oilqUNmeHO`hkKQr|DHc9Ip_=&Q?ReoyUxdbr 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 Pp@EW/OCIO-configs/Maya2022-default/config.ocio"; + setAttr ".vtn" -type "string" "ACES 1.0 SDR-video (sRGB)"; + setAttr ".vn" -type "string" "ACES 1.0 SDR-video"; + setAttr ".dn" -type "string" "sRGB"; + setAttr ".wsn" -type "string" "ACEScg"; + setAttr ".otn" -type "string" "ACES 1.0 SDR-video (sRGB)"; + setAttr ".potn" -type "string" "ACES 1.0 SDR-video (sRGB)"; +select -ne :hardwareRenderGlobals; + setAttr ".ctrs" 256; + setAttr ".btrs" 512; +connectAttr "polySphere1.out" "pSphereShape1.i"; +relationship "link" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message"; +relationship "link" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message"; +relationship "shadowLink" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message"; +relationship "shadowLink" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message"; +connectAttr "layerManager.dli[0]" "defaultLayer.id"; +connectAttr "renderLayerManager.rlmi[0]" "defaultRenderLayer.rlid"; +connectAttr "defaultRenderLayer.msg" ":defaultRenderingList1.r" -na; +connectAttr "pSphereShape1.iog" ":initialShadingGroup.dsm" -na; +// End of componentsPicking.ma From 108f8cb7a54b9c3f4a66d59a9067630cf1b896c9 Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Fri, 21 Jun 2024 15:29:51 +0200 Subject: [PATCH 2/7] Fix a bug found by the tests. --- .../adapters/renderItemAdapter.cpp | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp b/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp index 4e2ad6d9ed..b5fe23568d 100644 --- a/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp +++ b/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp @@ -380,25 +380,29 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data } } - if (topoChanged && (vertexCounts.size())) { + if (topoChanged) { switch (GetPrimitive()) { case MGeometry::Primitive::kTriangles:{ static const bool passNormalsToHydra = MayaHydraSceneIndex::passNormalsToHydra(); - if (passNormalsToHydra){ - _topology.reset(new HdMeshTopology( - PxOsdOpenSubdivTokens->none,//For the OGS normals vertex buffer to be used, we need to use PxOsdOpenSubdivTokens->none - UsdGeomTokens->rightHanded, - vertexCounts, - vertexIndices)); - } else{ - _topology.reset(new HdMeshTopology( - (GetMayaHydraSceneIndex()->GetParams().displaySmoothMeshes - || GetDisplayStyle().refineLevel > 0) - ? PxOsdOpenSubdivTokens->catmullClark - : PxOsdOpenSubdivTokens->none, - UsdGeomTokens->rightHanded, - vertexCounts, - vertexIndices)); + if (vertexCounts.size()) { + if (passNormalsToHydra) { + _topology.reset(new HdMeshTopology( + PxOsdOpenSubdivTokens + ->none, // For the OGS normals vertex buffer to be used, we need to use + // PxOsdOpenSubdivTokens->none + UsdGeomTokens->rightHanded, + vertexCounts, + vertexIndices)); + } else { + _topology.reset(new HdMeshTopology( + (GetMayaHydraSceneIndex()->GetParams().displaySmoothMeshes + || GetDisplayStyle().refineLevel > 0) + ? PxOsdOpenSubdivTokens->catmullClark + : PxOsdOpenSubdivTokens->none, + UsdGeomTokens->rightHanded, + vertexCounts, + vertexIndices)); + } } } break; From f8472eabd7a75e232ac85c73f8581ec1cc17350d Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Mon, 24 Jun 2024 14:35:46 +0200 Subject: [PATCH 3/7] HYDRA-30 Support components picking mode and hilite list for nodes --- .../sceneIndex/mayaHydraSceneIndex.cpp | 39 +++++++++++++------ .../sceneIndex/mayaHydraSceneIndex.h | 6 ++- lib/mayaHydra/mayaPlugin/renderOverride.cpp | 24 ++++++++---- lib/mayaHydra/mayaPlugin/renderOverride.h | 3 +- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp index 1300d9f17c..3f5391a7be 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -738,7 +740,8 @@ bool MayaHydraSceneIndex::AddPickHitToSelectionList( const HdxPickHit& hit, const MHWRender::MSelectionInfo& /* selectInfo */, MSelectionList& selectionList, - MPointArray& worldSpaceHitPts) + MPointArray& worldSpaceHitPts, + bool& isOneNodeInComponentsPickingMode) { SdfPath hitId = hit.objectId; // validate that hit is indeed a maya item. Alternatively, the rprim hit could be an rprim @@ -746,18 +749,32 @@ bool MayaHydraSceneIndex::AddPickHitToSelectionList( if (hitId.HasPrefix(GetRprimPath())) { _FindAdapter( hitId, - [&selectionList, &worldSpaceHitPts, &hit](MayaHydraRenderItemAdapter* a) { + [&selectionList, &worldSpaceHitPts, &hit, &isOneNodeInComponentsPickingMode](MayaHydraRenderItemAdapter* a) { // prepare the selection path of the hit item, the transform path is expected if available const auto& itemPath = a->GetDagPath(); - MDagPath selectPath; - if (MS::kSuccess != MDagPath::getAPathTo(itemPath.transform(), selectPath)) { - selectPath = itemPath; - } - selectionList.add(selectPath); - worldSpaceHitPts.append( - hit.worldSpaceHitPoint[0], - hit.worldSpaceHitPoint[1], - hit.worldSpaceHitPoint[2]); + + //Is the picked node in components selection mode ? If so it is in the hilite list + MSelectionList hiliteList; + MGlobal::getHiliteList(hiliteList); + MItSelectionList selListIter(hiliteList, MFn::kMesh);//Iterate on meshes only + for (; !selListIter.isDone(); selListIter.next()) { + MDagPath dagPath; + selListIter.getDagPath(dagPath); + if (itemPath == dagPath){ + isOneNodeInComponentsPickingMode = true; + return; + } + } + + MDagPath selectPath; + if (MS::kSuccess != MDagPath::getAPathTo(itemPath.transform(), selectPath)) { + selectPath = itemPath; + } + selectionList.add(selectPath); + worldSpaceHitPts.append( + hit.worldSpaceHitPoint[0], + hit.worldSpaceHitPoint[1], + hit.worldSpaceHitPoint[2]); }, _renderItemsAdapters); return true; diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h index c0fe6fd484..9f33314f45 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h @@ -120,12 +120,14 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public // Populate data from Maya void Populate(); - // Add hydra pick points and items to Maya's selection list + // Add hydra pick points and items to Maya's selection list, if isOneNodeInComponentsPickingMode is true when returning from this function, + // then one of the picked node is on the hilite list meaning we are selecting one of its components bool AddPickHitToSelectionList( const HdxPickHit& hit, const MHWRender::MSelectionInfo& selectInfo, MSelectionList& selectionList, - MPointArray& worldSpaceHitPts); + MPointArray& worldSpaceHitPts, + bool& isOneNodeInComponentsPickingMode); // Insert a primitive to hydra scene void InsertPrim( diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index de60dd5cae..ce83ceaa4f 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -363,8 +363,9 @@ PXR_NAMESPACE_OPEN_SCOPE class MtohRenderOverride::PickHandlerBase { public: + //isOneNodeInComponentsPickingMode will be true if one of the picked node is in components picking mode virtual bool handlePickHit( - const PickInput& pickInput, PickOutput& pickOutput + const PickInput& pickInput, PickOutput& pickOutput, bool& isOneNodeInComponentsPickingMode ) const = 0; protected: @@ -426,7 +427,7 @@ class MayaPickHandler : public MtohRenderOverride::PickHandlerBase { PickHandlerBase(renderOverride) {} bool handlePickHit( - const PickInput& pickInput, PickOutput& pickOutput + const PickInput& pickInput, PickOutput& pickOutput, bool& isOneNodeInComponentsPickingMode ) const override { if (!mayaSceneIndex()) { @@ -442,7 +443,7 @@ class MayaPickHandler : public MtohRenderOverride::PickHandlerBase { return mayaSceneIndex()->AddPickHitToSelectionList( pickInput.pickHit, pickInput.pickInfo, - pickOutput.mayaSelection, pickOutput.mayaWorldSpaceHitPts + pickOutput.mayaSelection, pickOutput.mayaWorldSpaceHitPts, isOneNodeInComponentsPickingMode ); } }; @@ -493,7 +494,7 @@ class UsdPickHandler : public MtohRenderOverride::PickHandlerBase { } bool handlePickHit( - const PickInput& pickInput, PickOutput& pickOutput + const PickInput& pickInput, PickOutput& pickOutput, bool& ) const override { if (!sceneIndexRegistry()) { @@ -1701,7 +1702,8 @@ void MtohRenderOverride::_PopulateSelectionList( const HdxPickHitVector& hits, const MHWRender::MSelectionInfo& selectInfo, MSelectionList& selectionList, - MPointArray& worldSpaceHitPts) + MPointArray& worldSpaceHitPts, + bool& isOneNodeInComponentsPickingMode) { if (hits.empty() || !_mayaHydraSceneIndex || !_ufeSn) { return; @@ -1713,7 +1715,10 @@ void MtohRenderOverride::_PopulateSelectionList( for (const HdxPickHit& hit : hits) { PickInput pickInput(hit, selectInfo); - _PickHandler(hit)->handlePickHit(pickInput, pickOutput); + _PickHandler(hit)->handlePickHit(pickInput, pickOutput, isOneNodeInComponentsPickingMode); + if (isOneNodeInComponentsPickingMode){ + return;//No need to continue iterating, we'll use maya/OGS to do the components selection + } } } @@ -1880,7 +1885,12 @@ bool MtohRenderOverride::select( } } - _PopulateSelectionList(outHits, selectInfo, selectionList, worldSpaceHitPts); + //isOneNodeInComponentsPickingMode will be true if one of the picked node is in components picking mode + bool isOneNodeInComponentsPickingMode = false; + _PopulateSelectionList(outHits, selectInfo, selectionList, worldSpaceHitPts, isOneNodeInComponentsPickingMode); + if (isOneNodeInComponentsPickingMode){ + return false;//When being in components picking on a node, returning false will use maya/OGS for components selection + } return true; } diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.h b/lib/mayaHydra/mayaPlugin/renderOverride.h index 409b6ebed6..960c941cc8 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.h +++ b/lib/mayaHydra/mayaPlugin/renderOverride.h @@ -194,7 +194,8 @@ class MtohRenderOverride : public MHWRender::MRenderOverride const HdxPickHitVector& hits, const MHWRender::MSelectionInfo& selectInfo, MSelectionList& selectionList, - MPointArray& worldSpaceHitPts); + MPointArray& worldSpaceHitPts, + bool& isOneNodeInComponentsPickingMode); void _AddPluginSelectionHighlighting(); From a39367ad15fbe87c0246fcf5f2ddd401cd8a19d1 Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Tue, 25 Jun 2024 15:35:55 +0200 Subject: [PATCH 4/7] Fix : color preference for polygons faces not updated in Hydra when you changed it in the UI. --- .../hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp | 5 +++-- .../hydraExtensions/sceneIndex/mayaHydraSceneIndex.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp index 3f5391a7be..176a25242d 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp @@ -394,7 +394,6 @@ SdfPath MayaHydraSceneIndex::_mayaDefaultMaterialPath; // Common to all scene in VtValue MayaHydraSceneIndex::_mayaDefaultMaterial; SdfPath MayaHydraSceneIndex::_mayaDefaultLightPath; // Common to all scene indexes SdfPath MayaHydraSceneIndex::_mayaFacesSelectionMaterialPath; // Common to all scene indexes -VtValue MayaHydraSceneIndex::_mayaFacesSelectionMaterial; MayaHydraSceneIndex::MayaHydraSceneIndex( MayaHydraInitData& initData, @@ -416,7 +415,7 @@ MayaHydraSceneIndex::MayaHydraSceneIndex( _mayaFacesSelectionMaterialPath = SdfPath::AbsoluteRootPath().AppendChild( _tokens ->MayaFacesSelectionMaterial); // Is an absolute path, not linked to a scene index - _mayaFacesSelectionMaterial = MayaHydraSceneIndex::CreateMayaFacesSelectionMaterial(); + _fallbackMaterial = SdfPath::EmptyPath(); // Empty path for hydra fallback material }); @@ -426,6 +425,8 @@ MayaHydraSceneIndex::MayaHydraSceneIndex( AddPrims({ { _mayaFacesSelectionMaterialPath, HdPrimTypeTokens->material, mayaHydraFacesSelectionMaterialDataSource } }); + //Always Create the material since it will update the color from the preferences if it has changed. + _mayaFacesSelectionMaterial = MayaHydraSceneIndex::CreateMayaFacesSelectionMaterial(); } MayaHydraSceneIndex::~MayaHydraSceneIndex() diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h index 9f33314f45..2939e4e368 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h @@ -331,7 +331,7 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public /// _mayaFacesSelectionMaterialPath is a path to a Hydra material used to display the faces selection on nodes when being in components selection mode static SdfPath _mayaFacesSelectionMaterialPath; /// _mayaFacesSelectionMaterial is a Hydra material used to display the faces selection on nodes when being in components selection mode - static VtValue _mayaFacesSelectionMaterial; + VtValue _mayaFacesSelectionMaterial; // Default light GlfSimpleLight _mayaDefaultLight; From 5147fac77d1cdf9949dd5dc3ccb44f915ce8a0eb Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Wed, 26 Jun 2024 10:19:45 +0200 Subject: [PATCH 5/7] Remove from the pickhandler the maya specific function to check for components picking mode on nodes --- .../sceneIndex/mayaHydraSceneIndex.cpp | 57 +++++++++++++------ .../sceneIndex/mayaHydraSceneIndex.h | 6 +- lib/mayaHydra/mayaPlugin/renderOverride.cpp | 35 ++++++++---- lib/mayaHydra/mayaPlugin/renderOverride.h | 4 +- 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp index 176a25242d..7610bf66fe 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp @@ -737,36 +737,61 @@ SdfPath MayaHydraSceneIndex::SetCameraViewport(const MDagPath& camPath, const Gf return {}; } -bool MayaHydraSceneIndex::AddPickHitToSelectionList( - const HdxPickHit& hit, - const MHWRender::MSelectionInfo& /* selectInfo */, - MSelectionList& selectionList, - MPointArray& worldSpaceHitPts, - bool& isOneNodeInComponentsPickingMode) +bool MayaHydraSceneIndex::IsPickedNodeInComponentsPickingMode(const HdxPickHit& hit)const { + // Is the picked node in components selection mode ? If so it is in the hilite list + MSelectionList hiliteList; + MGlobal::getHiliteList(hiliteList); + if (hiliteList.isEmpty()){ + return false; + } + + bool isOneNodeInComponentsPickingMode = false; + SdfPath hitId = hit.objectId; - // validate that hit is indeed a maya item. Alternatively, the rprim hit could be an rprim - // defined by a scene index such as maya usd. if (hitId.HasPrefix(GetRprimPath())) { _FindAdapter( hitId, - [&selectionList, &worldSpaceHitPts, &hit, &isOneNodeInComponentsPickingMode](MayaHydraRenderItemAdapter* a) { - // prepare the selection path of the hit item, the transform path is expected if available + [&hit, &hiliteList, &isOneNodeInComponentsPickingMode]( + MayaHydraRenderItemAdapter* a) { + // prepare the selection path of the hit item, the transform path is expected if + // available const auto& itemPath = a->GetDagPath(); - - //Is the picked node in components selection mode ? If so it is in the hilite list - MSelectionList hiliteList; - MGlobal::getHiliteList(hiliteList); - MItSelectionList selListIter(hiliteList, MFn::kMesh);//Iterate on meshes only + + // Is the picked node in components selection mode ? If so it is in the hilite list + MItSelectionList selListIter(hiliteList, MFn::kMesh); // Iterate on meshes only for (; !selListIter.isDone(); selListIter.next()) { MDagPath dagPath; selListIter.getDagPath(dagPath); - if (itemPath == dagPath){ + if (itemPath == dagPath) { isOneNodeInComponentsPickingMode = true; return; } } + }, + _renderItemsAdapters); + return isOneNodeInComponentsPickingMode; + } + + return false; +} +bool MayaHydraSceneIndex::AddPickHitToSelectionList( + const HdxPickHit& hit, + const MHWRender::MSelectionInfo& /* selectInfo */, + MSelectionList& selectionList, + MPointArray& worldSpaceHitPts) +{ + SdfPath hitId = hit.objectId; + // validate that hit is indeed a maya item. Alternatively, the rprim hit could be an rprim + // defined by a scene index such as maya usd. + if (hitId.HasPrefix(GetRprimPath())) { + _FindAdapter( + hitId, + [&selectionList, &worldSpaceHitPts, &hit](MayaHydraRenderItemAdapter* a) { + // prepare the selection path of the hit item, the transform path is expected if available + const auto& itemPath = a->GetDagPath(); + MDagPath selectPath; if (MS::kSuccess != MDagPath::getAPathTo(itemPath.transform(), selectPath)) { selectPath = itemPath; diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h index 2939e4e368..70c3eae62f 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h @@ -126,8 +126,10 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public const HdxPickHit& hit, const MHWRender::MSelectionInfo& selectInfo, MSelectionList& selectionList, - MPointArray& worldSpaceHitPts, - bool& isOneNodeInComponentsPickingMode); + MPointArray& worldSpaceHitPts); + + bool IsPickedNodeInComponentsPickingMode(const HdxPickHit& hit)const; + // Insert a primitive to hydra scene void InsertPrim( diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index ce83ceaa4f..28be98ab04 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -365,8 +365,7 @@ class MtohRenderOverride::PickHandlerBase { //isOneNodeInComponentsPickingMode will be true if one of the picked node is in components picking mode virtual bool handlePickHit( - const PickInput& pickInput, PickOutput& pickOutput, bool& isOneNodeInComponentsPickingMode - ) const = 0; + const PickInput& pickInput, PickOutput& pickOutput) const = 0; protected: @@ -427,8 +426,7 @@ class MayaPickHandler : public MtohRenderOverride::PickHandlerBase { PickHandlerBase(renderOverride) {} bool handlePickHit( - const PickInput& pickInput, PickOutput& pickOutput, bool& isOneNodeInComponentsPickingMode - ) const override + const PickInput& pickInput, PickOutput& pickOutput) const override { if (!mayaSceneIndex()) { TF_FATAL_ERROR("Picking called while no Maya scene index exists"); @@ -443,7 +441,7 @@ class MayaPickHandler : public MtohRenderOverride::PickHandlerBase { return mayaSceneIndex()->AddPickHitToSelectionList( pickInput.pickHit, pickInput.pickInfo, - pickOutput.mayaSelection, pickOutput.mayaWorldSpaceHitPts, isOneNodeInComponentsPickingMode + pickOutput.mayaSelection, pickOutput.mayaWorldSpaceHitPts ); } }; @@ -494,7 +492,7 @@ class UsdPickHandler : public MtohRenderOverride::PickHandlerBase { } bool handlePickHit( - const PickInput& pickInput, PickOutput& pickOutput, bool& + const PickInput& pickInput, PickOutput& pickOutput ) const override { if (!sceneIndexRegistry()) { @@ -1703,7 +1701,7 @@ void MtohRenderOverride::_PopulateSelectionList( const MHWRender::MSelectionInfo& selectInfo, MSelectionList& selectionList, MPointArray& worldSpaceHitPts, - bool& isOneNodeInComponentsPickingMode) + bool& isOneMayaNodeInComponentsPickingMode) { if (hits.empty() || !_mayaHydraSceneIndex || !_ufeSn) { return; @@ -1711,17 +1709,32 @@ void MtohRenderOverride::_PopulateSelectionList( PickOutput pickOutput(selectionList, worldSpaceHitPts, _ufeSn); + // Is the picked node in components selection mode ? If so it is in the hilite list + MSelectionList hiliteList; + MGlobal::getHiliteList(hiliteList); + const bool hiliteListIsEmpty = hiliteList.isEmpty(); + MStatus status; for (const HdxPickHit& hit : hits) { PickInput pickInput(hit, selectInfo); - - _PickHandler(hit)->handlePickHit(pickInput, pickOutput, isOneNodeInComponentsPickingMode); - if (isOneNodeInComponentsPickingMode){ - return;//No need to continue iterating, we'll use maya/OGS to do the components selection + auto pickHandler = _PickHandler(hit); + if (!hiliteListIsEmpty && _IsMayaPickHandler(pickHandler)){ + // Maya does not create Hydra instances, so if the pick hit instancer + // ID isn't empty, it's not a Maya pick hit. + if (_mayaHydraSceneIndex && pickInput.pickHit.instancerId.IsEmpty() && _mayaHydraSceneIndex->IsPickedNodeInComponentsPickingMode(pickInput.pickHit)){ + isOneMayaNodeInComponentsPickingMode = true; + return; + } } + pickHandler->handlePickHit(pickInput, pickOutput); } } +bool MtohRenderOverride::_IsMayaPickHandler(const MtohRenderOverride::PickHandlerBase* pickHandler)const +{ + return pickHandler == _pickHandlers[0].get(); +} + const MtohRenderOverride::PickHandlerBase* MtohRenderOverride::_PickHandler(const HdxPickHit& pickHit) const { diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.h b/lib/mayaHydra/mayaPlugin/renderOverride.h index 960c941cc8..76c19d7e74 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.h +++ b/lib/mayaHydra/mayaPlugin/renderOverride.h @@ -195,12 +195,14 @@ class MtohRenderOverride : public MHWRender::MRenderOverride const MHWRender::MSelectionInfo& selectInfo, MSelectionList& selectionList, MPointArray& worldSpaceHitPts, - bool& isOneNodeInComponentsPickingMode); + bool& isOneMayaNodeInComponentsPickingMode); void _AddPluginSelectionHighlighting(); bool _NeedToRecreateTheSceneIndicesChain(unsigned int currentDisplayStyle, bool currentUseDefaultMaterial, bool xRayEnabled); + bool _IsMayaPickHandler(const MtohRenderOverride::PickHandlerBase* pickHandler)const; + // Determine the pick handler which should handle a pick hit, to transform // the pick hit into a selection. const PickHandlerBase* _PickHandler(const HdxPickHit& hit) const; From d16fb808fb6252f2dcabbed9590d9424809cf49c Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Thu, 27 Jun 2024 09:30:21 +0200 Subject: [PATCH 6/7] Fixes from code review. --- .../adapters/renderItemAdapter.cpp | 13 ++++++------- .../sceneIndex/mayaHydraSceneIndex.cpp | 11 +++++------ .../sceneIndex/mayaHydraSceneIndex.h | 3 +-- lib/mayaHydra/mayaPlugin/renderOverride.cpp | 16 ++++++++++------ .../mayaUsd/render/mayaToHydra/CMakeLists.txt | 2 +- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp b/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp index b5fe23568d..c51b49a90d 100644 --- a/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp +++ b/lib/mayaHydra/hydraExtensions/adapters/renderItemAdapter.cpp @@ -142,7 +142,7 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data // const bool isNew = flags & MViewportScene::MVS_new; //not used yet const bool visible = data._flags & MVS::MVS_visible; const bool matrixChanged = data._flags & MVS::MVS_changedMatrix; - bool geomChanged = (data._flags & MVS::MVS_changedGeometry) || positionsHaveBeenReset;//Non const as we may modify it later + bool geomChanged = (data._flags & MVS::MVS_changedGeometry) || positionsHaveBeenReset;//Non const as we may modify it later => Temp workaround for a bug in Maya MAYA-134200 const bool topoChanged = (data._flags & MVS::MVS_changedTopo) || positionsHaveBeenReset; const bool visibChanged = data._flags & MVS::MVS_changedVisibility; const bool effectChanged = data._flags & MVS::MVS_changedEffect; @@ -197,7 +197,7 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data if ((!geomChanged && topoChanged) && vertexBuffercount) { //With face components selection, we have topoChanged which is true but geomChanged is false, but this is wrong, the number of vertices may have changed. //We want to check here if we also need to update the geometry if the number of vertices is different from what is stored already - for (int vbIdx = 0; vbIdx < vertexBuffercount; vbIdx++) { + for (int vbIdx = 0; (vbIdx < vertexBuffercount) && (!geomChanged); vbIdx++) { MVertexBuffer* mvb = geom->vertexBuffer(vbIdx); if (!mvb) { continue; @@ -211,8 +211,7 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data MVertexBuffer* verts = mvb; const unsigned int originalVertexCount = verts->vertexCount(); if (_positions.size() != originalVertexCount) {//Is it different ? - geomChanged = true; - vbIdx = vertexBuffercount; //Stop looping + geomChanged = true;//this will stop the loop } } break; default: break; @@ -386,10 +385,10 @@ void MayaHydraRenderItemAdapter::UpdateFromDelta(const UpdateFromDeltaData& data static const bool passNormalsToHydra = MayaHydraSceneIndex::passNormalsToHydra(); if (vertexCounts.size()) { if (passNormalsToHydra) { + // For the OGS normals vertex buffer to be used, we need to use + // PxOsdOpenSubdivTokens->none _topology.reset(new HdMeshTopology( - PxOsdOpenSubdivTokens - ->none, // For the OGS normals vertex buffer to be used, we need to use - // PxOsdOpenSubdivTokens->none + PxOsdOpenSubdivTokens->none, UsdGeomTokens->rightHanded, vertexCounts, vertexIndices)); diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp index 7610bf66fe..0982e08092 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.cpp @@ -413,8 +413,7 @@ MayaHydraSceneIndex::MayaHydraSceneIndex( _mayaDefaultLightPath = SdfPath::AbsoluteRootPath().AppendChild(_tokens->DefaultMayaLight); _mayaDefaultMaterial = MayaHydraSceneIndex::CreateMayaDefaultMaterial(); _mayaFacesSelectionMaterialPath = SdfPath::AbsoluteRootPath().AppendChild( - _tokens - ->MayaFacesSelectionMaterial); // Is an absolute path, not linked to a scene index + _tokens->MayaFacesSelectionMaterial); _fallbackMaterial = SdfPath::EmptyPath(); // Empty path for hydra fallback material }); @@ -746,13 +745,13 @@ bool MayaHydraSceneIndex::IsPickedNodeInComponentsPickingMode(const HdxPickHit& return false; } - bool isOneNodeInComponentsPickingMode = false; + bool isOneMayaNodeInComponentsPickingMode = false; SdfPath hitId = hit.objectId; if (hitId.HasPrefix(GetRprimPath())) { _FindAdapter( hitId, - [&hit, &hiliteList, &isOneNodeInComponentsPickingMode]( + [&hit, &hiliteList, &isOneMayaNodeInComponentsPickingMode]( MayaHydraRenderItemAdapter* a) { // prepare the selection path of the hit item, the transform path is expected if // available @@ -764,13 +763,13 @@ bool MayaHydraSceneIndex::IsPickedNodeInComponentsPickingMode(const HdxPickHit& MDagPath dagPath; selListIter.getDagPath(dagPath); if (itemPath == dagPath) { - isOneNodeInComponentsPickingMode = true; + isOneMayaNodeInComponentsPickingMode = true; return; } } }, _renderItemsAdapters); - return isOneNodeInComponentsPickingMode; + return isOneMayaNodeInComponentsPickingMode; } return false; diff --git a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h index 70c3eae62f..d1cf0054d5 100644 --- a/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h +++ b/lib/mayaHydra/hydraExtensions/sceneIndex/mayaHydraSceneIndex.h @@ -120,8 +120,7 @@ class MAYAHYDRALIB_API MayaHydraSceneIndex : public HdRetainedSceneIndex, public // Populate data from Maya void Populate(); - // Add hydra pick points and items to Maya's selection list, if isOneNodeInComponentsPickingMode is true when returning from this function, - // then one of the picked node is on the hilite list meaning we are selecting one of its components + // Add hydra pick points and items to Maya's selection list bool AddPickHitToSelectionList( const HdxPickHit& hit, const MHWRender::MSelectionInfo& selectInfo, diff --git a/lib/mayaHydra/mayaPlugin/renderOverride.cpp b/lib/mayaHydra/mayaPlugin/renderOverride.cpp index 28be98ab04..53315d9aa1 100644 --- a/lib/mayaHydra/mayaPlugin/renderOverride.cpp +++ b/lib/mayaHydra/mayaPlugin/renderOverride.cpp @@ -363,7 +363,6 @@ PXR_NAMESPACE_OPEN_SCOPE class MtohRenderOverride::PickHandlerBase { public: - //isOneNodeInComponentsPickingMode will be true if one of the picked node is in components picking mode virtual bool handlePickHit( const PickInput& pickInput, PickOutput& pickOutput) const = 0; @@ -1820,7 +1819,12 @@ bool MtohRenderOverride::select( "MtohRenderOverride::select", "MtohRenderOverride::select"); #endif - + /* + * There are 2 modes of selection picking for components in maya : + * 1) You can be in components picking mode, this setting is global.This is detected in the function "isInComponentsPickingMode(selectInfo)" + * 2) The second mode is when you right click on a node and choose a component to pick it (e.g : Face), this is + * where we use the variable "isOneNodeInComponentsPickingMode" to detect that case, later in this function. + */ if (isInComponentsPickingMode(selectInfo)) { return false; //When being in components picking, returning false will use maya/OGS for components selection } @@ -1898,10 +1902,10 @@ bool MtohRenderOverride::select( } } - //isOneNodeInComponentsPickingMode will be true if one of the picked node is in components picking mode - bool isOneNodeInComponentsPickingMode = false; - _PopulateSelectionList(outHits, selectInfo, selectionList, worldSpaceHitPts, isOneNodeInComponentsPickingMode); - if (isOneNodeInComponentsPickingMode){ + //isOneMayaNodeInComponentsPickingMode will be true if one of the picked node is in components picking mode + bool isOneMayaNodeInComponentsPickingMode = false; + _PopulateSelectionList(outHits, selectInfo, selectionList, worldSpaceHitPts, isOneMayaNodeInComponentsPickingMode); + if (isOneMayaNodeInComponentsPickingMode){ return false;//When being in components picking on a node, returning false will use maya/OGS for components selection } return true; diff --git a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt index f5009722e6..1b88840cea 100644 --- a/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt +++ b/test/lib/mayaUsd/render/mayaToHydra/CMakeLists.txt @@ -46,7 +46,7 @@ set(INTERACTIVE_TEST_SCRIPT_FILES testDataProducerSelHighlight.py|skipOnPlatform:osx testPassingNormalsOnMayaNative.py testViewportFilters.py - testMayaComponentsPicking.py + testMayaComponentsPicking.py cpp/testColorPreferences.py cpp/testCppFramework.py cpp/testMayaSceneFlattening.py From 3698207522bee80a608b624479139f86c56f3a5f Mon Sep 17 00:00:00 2001 From: "ADS\\lanierd" Date: Thu, 27 Jun 2024 16:11:26 +0200 Subject: [PATCH 7/7] Fix scene maya version --- .../testMayaComponentsPicking/testMayaComponentsPicking.ma | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/testSamples/testMayaComponentsPicking/testMayaComponentsPicking.ma b/test/testSamples/testMayaComponentsPicking/testMayaComponentsPicking.ma index 90ff2e265a..0c75f0634d 100644 --- a/test/testSamples/testMayaComponentsPicking/testMayaComponentsPicking.ma +++ b/test/testSamples/testMayaComponentsPicking/testMayaComponentsPicking.ma @@ -1,8 +1,8 @@ -//Maya ASCII 2026ff01 scene +//Maya ASCII 2025ff01 scene //Name: componentsPicking.ma //Last modified: Wed, Jun 19, 2024 06:08:46 PM //Codeset: 1252 -requires maya "2026ff01"; +requires maya "2025ff01"; currentUnit -l centimeter -a degree -t film; fileInfo "application" "maya"; fileInfo "product" "Maya 2025";