diff --git a/cmake/mayausd_version.info b/cmake/mayausd_version.info index f8e1508b33..6c91d1cc9d 100644 --- a/cmake/mayausd_version.info +++ b/cmake/mayausd_version.info @@ -1,3 +1,3 @@ set(MAYAUSD_MAJOR_VERSION 0) -set(MAYAUSD_MINOR_VERSION 22) +set(MAYAUSD_MINOR_VERSION 23) set(MAYAUSD_PATCH_LEVEL 0) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index d8251c476e..4f9930057a 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,67 @@ # Changelog +## [v0.22.0] - 2023-02-15 + +**Build:** +* Skip test if UFE attribute metadata not available [#2834](https://github.com/Autodesk/maya-usd/pull/2834) +* Use the PXR namespace directive [#2827](https://github.com/Autodesk/maya-usd/pull/2827) +* Bump UFE version to 4.0 [#2819](https://github.com/Autodesk/maya-usd/pull/2819) +* Fix VP2 test [#2787](https://github.com/Autodesk/maya-usd/pull/2787) +* Fix empty values for remapUVSetsTo [#2784](https://github.com/Autodesk/maya-usd/pull/2784) +* Fix USD Min build after outliner rework [#2781](https://github.com/Autodesk/maya-usd/pull/2781) + +**Translation Framework:** +* Improve NURBS export [#2835](https://github.com/Autodesk/maya-usd/pull/2835) +* Import as anim cache [#2829](https://github.com/Autodesk/maya-usd/pull/2829) +* Token updates in preparation for Schema Versioning [#2826](https://github.com/Autodesk/maya-usd/pull/2826) +* Invalid beziers [#2813](https://github.com/Autodesk/maya-usd/pull/2813) +* Fix material export when preserving UVset names [#2776](https://github.com/Autodesk/maya-usd/pull/2776) +* Export Normals by default and modify docs to match code behaviour [#2768](https://github.com/Autodesk/maya-usd/pull/2768) + +**Workflow:** +* Relative File Paths: + * Save USD root file relative to scene file [#2854](https://github.com/Autodesk/maya-usd/pull/2854) [#2839](https://github.com/Autodesk/maya-usd/pull/2839) + * Load my USD root file as relative to the scene file [#2848](https://github.com/Autodesk/maya-usd/pull/2848) +* Invalid extension in Layer editor would cause crash [#2847](https://github.com/Autodesk/maya-usd/pull/2847) [#2845](https://github.com/Autodesk/maya-usd/pull/2845) +* Clean options of materials scope name [#2840](https://github.com/Autodesk/maya-usd/pull/2840) +* Preserve NodeGraph boundaries on duplicate [#2836](https://github.com/Autodesk/maya-usd/pull/2836) +* Prevents a hang that can occur when forming certain connections [#2828](https://github.com/Autodesk/maya-usd/pull/2828) +* Prettify: Add space on lower to upper transition [#2824](https://github.com/Autodesk/maya-usd/pull/2824) +* Add Ufe log on console to notify shader attribute without default value [#2823](https://github.com/Autodesk/maya-usd/pull/2823) +* Prevent string conversions to throw exceptions with empty value [#2822](https://github.com/Autodesk/maya-usd/pull/2822) +* Fix plugInfo to use relative path [#2821](https://github.com/Autodesk/maya-usd/pull/2821) +* Fix USD export curve options [#2820](https://github.com/Autodesk/maya-usd/pull/2820) +* Shorten nice names by trimming schema name [#2818](https://github.com/Autodesk/maya-usd/pull/2818) +* Add reparenting rules for Shaders, NodeGraphs and Materials [#2811](https://github.com/Autodesk/maya-usd/pull/2811) +* Allow native file dialog when loading layers [#2807](https://github.com/Autodesk/maya-usd/pull/2807) +* Fix crash when editing two prims [#2806](https://github.com/Autodesk/maya-usd/pull/2806) +* Add scene index plugin code [#2805](https://github.com/Autodesk/maya-usd/pull/2805) +* Correctly identify orphaned edited prims [#2803](https://github.com/Autodesk/maya-usd/pull/2803) +* mayaUsd.ufe.stagePath() returns incorrect value [#2801](https://github.com/Autodesk/maya-usd/pull/2801) +* Delete compound attributes does not remove their connections [#2800](https://github.com/Autodesk/maya-usd/pull/2800) +* Improve diagnostics [#2799](https://github.com/Autodesk/maya-usd/pull/2799) +* Interaction between deactivation and cancel edit [#2794](https://github.com/Autodesk/maya-usd/pull/2794) +* Merge top UFE classification for glsfx and USD shader [#2791](https://github.com/Autodesk/maya-usd/pull/2791) +* UFE USD camera commands [#2789](https://github.com/Autodesk/maya-usd/pull/2789) +* Correctly expose Material and NodeGraph attributes in Maya Attribute Editor [#2788](https://github.com/Autodesk/maya-usd/pull/2788) +* Fix a bug where materials got created in the wrong scope [#2783](https://github.com/Autodesk/maya-usd/pull/2783) +* Outliner rework of Material items [#2775](https://github.com/Autodesk/maya-usd/pull/2775) +* Correctly disable merge to USD [#2769](https://github.com/Autodesk/maya-usd/pull/2769) +* merge-to-USD into variants [#2764](https://github.com/Autodesk/maya-usd/pull/2764) +* Auto re-edit when merging a Maya Reference [#2762](https://github.com/Autodesk/maya-usd/pull/2762) + +**Render:** +* Adding a MaterialX v1.38.5 update for closures [#2858](https://github.com/Autodesk/maya-usd/pull/2858) +* Enable instances to determine non empty stage in preparation for USD v0.23.02 [#2844](https://github.com/Autodesk/maya-usd/pull/2844) +* UDIM: Use default LOD values in anisotropic mode [#2816](https://github.com/Autodesk/maya-usd/pull/2816) +* Remove frequent warning in Arnold/RenderMan shading workflows [#2814](https://github.com/Autodesk/maya-usd/pull/2814) +* Fix opacity computations for MaterialX and USD materials [#2790](https://github.com/Autodesk/maya-usd/pull/2790) +* Implement display layer's hide-on-playback flag for instanced geometry [#2780](https://github.com/Autodesk/maya-usd/pull/2780) + +**Documentation:** +* Update documentation of the export command [#2808](https://github.com/Autodesk/maya-usd/pull/2808) + + ## [v0.21.0] - 2022-12-13 **Build:** @@ -620,7 +682,7 @@ - Avoid opening duplicated usd stages on Maya file load [#1727](https://github.com/Autodesk/maya-usd/pull/1727) - Fix excluded prim paths [#1726](https://github.com/Autodesk/maya-usd/pull/1726) - Add duplicate check during translator context deserialise based on prim path match [#1710](https://github.com/Autodesk/maya-usd/pull/1710) - - Ignored setting playback range if no time code found from USD [\#1686](https://github.com/Autodesk/maya-usd/pull/1686) + - Ignored setting playback range if no time code found from USD [#1686](https://github.com/Autodesk/maya-usd/pull/1686) - Use the API schema class UsdLuxLightAPI in place of USD typed class [#1701](https://github.com/Autodesk/maya-usd/pull/1701) - Adopt use of references when passing ReaderContext when reading RfM [#1653](https://github.com/Autodesk/maya-usd/pull/1653) - Update RfMLight translators to use codeless schemas [#1648](https://github.com/Autodesk/maya-usd/pull/1648) diff --git a/lib/mayaUsd/listeners/stageNoticeListener.cpp b/lib/mayaUsd/listeners/stageNoticeListener.cpp index db588022f2..2dadc8a457 100644 --- a/lib/mayaUsd/listeners/stageNoticeListener.cpp +++ b/lib/mayaUsd/listeners/stageNoticeListener.cpp @@ -64,6 +64,14 @@ void UsdMayaStageNoticeListener::SetStageLayerMutingChangedCallback( _UpdateStageContentsChangedRegistration(); } +void UsdMayaStageNoticeListener::SetStageEditTargetChangedCallback( + const StageEditTargetChangedCallback& callback) +{ + _stageEditTargetChangedCallback = callback; + + _UpdateStageContentsChangedRegistration(); +} + void UsdMayaStageNoticeListener::_UpdateStageContentsChangedRegistration() { if (_stage && _stageContentsChangedCallback) { @@ -109,6 +117,22 @@ void UsdMayaStageNoticeListener::_UpdateStageContentsChangedRegistration() TfNotice::Revoke(_stageLayerMutingChangedKey); } } + + if (_stage && _stageEditTargetChangedCallback) { + // Register for notices if we're not already listening. + if (!_stageEditTargetChangedKey.IsValid()) { + _stageEditTargetChangedKey = TfNotice::Register( + TfCreateWeakPtr(this), + &UsdMayaStageNoticeListener::_OnStageEditTargetChanged, + _stage); + } + } else { + // Either the stage or the callback is invalid, so stop listening for + // notices. + if (_stageEditTargetChangedKey.IsValid()) { + TfNotice::Revoke(_stageEditTargetChangedKey); + } + } } void UsdMayaStageNoticeListener::_OnStageContentsChanged( @@ -136,4 +160,12 @@ void UsdMayaStageNoticeListener::_OnStageLayerMutingChanged( } } +void UsdMayaStageNoticeListener::_OnStageEditTargetChanged( + const UsdNotice::StageEditTargetChanged& notice) const +{ + if (notice.GetStage() == _stage && _stageEditTargetChangedCallback) { + _stageEditTargetChangedCallback(notice); + } +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/listeners/stageNoticeListener.h b/lib/mayaUsd/listeners/stageNoticeListener.h index 788724d2ac..552ad51dd2 100644 --- a/lib/mayaUsd/listeners/stageNoticeListener.h +++ b/lib/mayaUsd/listeners/stageNoticeListener.h @@ -54,6 +54,8 @@ class UsdMayaStageNoticeListener : public TfWeakBase = std::function; using StageLayerMutingChangedCallback = std::function; + using StageEditTargetChangedCallback + = std::function; /// Sets the callback to be invoked when the listener receives a /// StageContentsChanged notice. @@ -66,10 +68,15 @@ class UsdMayaStageNoticeListener : public TfWeakBase void SetStageObjectsChangedCallback(const StageObjectsChangedCallback& callback); /// Sets the callback to be invoked when the listener receives a - /// ObjectsChanged notice. + /// LayerMutingChanged notice. MAYAUSD_CORE_PUBLIC void SetStageLayerMutingChangedCallback(const StageLayerMutingChangedCallback& callback); + /// Sets the callback to be invoked when the listener receives a + /// EditTargetChanged notice. + MAYAUSD_CORE_PUBLIC + void SetStageEditTargetChangedCallback(const StageEditTargetChangedCallback& callback); + private: UsdMayaStageNoticeListener(const UsdMayaStageNoticeListener&) = delete; UsdMayaStageNoticeListener& operator=(const UsdMayaStageNoticeListener&) = delete; @@ -86,12 +93,16 @@ class UsdMayaStageNoticeListener : public TfWeakBase TfNotice::Key _stageLayerMutingChangedKey {}; StageLayerMutingChangedCallback _stageLayerMutingChangedCallback {}; + TfNotice::Key _stageEditTargetChangedKey {}; + StageEditTargetChangedCallback _stageEditTargetChangedCallback {}; + void _UpdateStageContentsChangedRegistration(); void _OnStageContentsChanged(const UsdNotice::StageContentsChanged& notice) const; void _OnStageLayerMutingChanged(const UsdNotice::LayerMutingChanged& notice) const; void _OnStageObjectsChanged( const UsdNotice::ObjectsChanged& notice, const UsdStageWeakPtr& sender) const; + void _OnStageEditTargetChanged(const UsdNotice::StageEditTargetChanged& notice) const; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/nodes/layerManager.cpp b/lib/mayaUsd/nodes/layerManager.cpp index f1c04e348a..676cc7ae33 100644 --- a/lib/mayaUsd/nodes/layerManager.cpp +++ b/lib/mayaUsd/nodes/layerManager.cpp @@ -176,7 +176,8 @@ void convertAnonymousLayersRecursive( convertAnonymousLayersRecursive(subL, basename, stage); if (subL->IsAnonymous()) { - auto newLayer = MayaUsd::utils::saveAnonymousLayer(subL, parentPtr, basename); + auto newLayer + = MayaUsd::utils::saveAnonymousLayer(stage, subL, parentPtr, basename); if (subL == currentTarget) { stage->SetEditTarget(newLayer); } @@ -248,6 +249,7 @@ class LayerDatabase : public TfWeakBase void saveUsdLayerToMayaFile(SdfLayerRefPtr layer, bool asAnonymous); void clearProxies(); bool hasDirtyLayer() const; + void refreshProxiesToSave(); std::map _idToLayer; TfNotice::Key _onStageSetKey; @@ -520,6 +522,27 @@ bool LayerDatabase::getProxiesToSave(bool isExport) bool LayerDatabase::saveInteractionRequired() { return _proxiesToSave.size() > 0; } +static void refreshSavingInfo(StageSavingInfo& info) +{ + MFnDependencyNode fn; + MObject mobj = info.dagPath.node(); + fn.setObject(mobj); + if (!fn.isFromReferencedFile() && LayerDatabase::instance().supportedNodeType(fn.typeId())) { + MayaUsdProxyShapeBase* pShape = static_cast(fn.userNode()); + info.stage = pShape ? pShape->getUsdStage() : nullptr; + } +} + +void LayerDatabase::refreshProxiesToSave() +{ + for (StageSavingInfo& info : _proxiesToSave) { + refreshSavingInfo(info); + } + for (StageSavingInfo& info : _internalProxiesToSave) { + refreshSavingInfo(info); + } +} + bool LayerDatabase::saveUsd(bool isExport) { BatchSaveResult result = MayaUsd::kNotHandled; @@ -555,6 +578,12 @@ bool LayerDatabase::saveUsd(bool isExport) return true; } + // After the potentially partial save, we need to refresh the stages + // to be saved because the saving might have modified the proxy shape + // attributes and we need to re-evaluate these nodes so that the stages + // are re-created with the new attribute values if needed. + refreshProxiesToSave(); + if (MayaUsd::utils::kSaveToUSDFiles == opt) { result = saveUsdToUsdFiles(); } else { @@ -781,6 +810,9 @@ void LayerDatabase::convertAnonymousLayers( convertAnonymousLayersRecursive(root, proxyName, stage); + // Note: retrieve root again since it may have been changed by the call + // to convertAnonymousLayersRecursive + root = stage->GetRootLayer(); if (root->IsAnonymous()) { PXR_NS::SdfFileFormat::FileFormatArguments args; std::string newFileName = MayaUsd::utils::generateUniqueFileName(proxyName); diff --git a/lib/mayaUsd/nodes/proxyShapeBase.cpp b/lib/mayaUsd/nodes/proxyShapeBase.cpp index 5c0aea28a5..88ce63a6ce 100644 --- a/lib/mayaUsd/nodes/proxyShapeBase.cpp +++ b/lib/mayaUsd/nodes/proxyShapeBase.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -214,6 +215,21 @@ const int _shapeBaseProfilerCategory = MProfiler::addCategory( "ProxyShapeBase" #endif ); + +struct InComputeGuard +{ + InComputeGuard(MayaUsdProxyShapeBase& proxy) + : _proxy(proxy) + { + _proxy.in_compute++; + } + + ~InComputeGuard() { _proxy.in_compute--; } + +private: + MayaUsdProxyShapeBase& _proxy; +}; + } // namespace /* static */ @@ -510,6 +526,8 @@ void MayaUsdProxyShapeBase::postConstructor() /* virtual */ MStatus MayaUsdProxyShapeBase::compute(const MPlug& plug, MDataBlock& dataBlock) { + InComputeGuard inComputeGuard(*this); + if (plug == outTimeAttr || plug.isDynamic()) ProxyAccessor::compute(_usdAccessor, plug, dataBlock); @@ -990,6 +1008,7 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock) primPath = finalUsdStage->GetPseudoRoot().GetPath(); copyLoadRulesFromAttribute(*this, *finalUsdStage); copyLayerMutingFromAttribute(*this, *finalUsdStage); + copyTargetLayerFromAttribute(*this, *finalUsdStage); updateShareMode(sharedUsdStage, unsharedUsdStage, loadSet); } @@ -1089,12 +1108,6 @@ MStatus MayaUsdProxyShapeBase::computeOutStageData(MDataBlock& dataBlock) MProfilingScope computeOutStageDatacomputeOutStageData( _shapeBaseProfilerCategory, MProfiler::kColorE_L3, "Compute outStageData plug"); - struct in_computeGuard - { - in_computeGuard() { in_compute++; } - ~in_computeGuard() { in_compute--; } - } in_computeGuard; - MStatus retValue = MS::kSuccess; const bool isNormalContext = dataBlock.context().isNormal(); @@ -1193,6 +1206,11 @@ MStatus MayaUsdProxyShapeBase::computeOutStageData(MDataBlock& dataBlock) return _OnLayerMutingChanged(notice); }); + _stageNoticeListener.SetStageEditTargetChangedCallback( + [this](const UsdNotice::StageEditTargetChanged& notice) { + return _OnStageEditTargetChanged(notice); + }); + MayaUsdProxyStageSetNotice(*this).Send(); } @@ -1528,6 +1546,8 @@ MStatus MayaUsdProxyShapeBase::postEvaluation( // setGeometryDrawDirty, or no calls to setGeometryDrawDirty. // MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + InComputeGuard inComputeGuard(*this); + if (context.isNormal() && evalType == PostEvaluationEnum::kEvaluatedDirectly) { MDataBlock dataBlock = forceCache(); ProxyAccessor::syncCache(_usdAccessor, thisMObject(), dataBlock); @@ -1874,6 +1894,42 @@ void MayaUsdProxyShapeBase::_OnLayerMutingChanged(const UsdNotice::LayerMutingCh copyLayerMutingToAttribute(*stage, *this); } +static void copyTargetLayerOnIdle(void* data) +{ + MayaUsdProxyShapeBase* proxy = reinterpret_cast(data); + if (!proxy) + return; + + const auto stage = proxy->getUsdStage(); + if (!stage) + return; + + copyTargetLayerToAttribute(*stage, *proxy); +} + +void MayaUsdProxyShapeBase::_OnStageEditTargetChanged( + const UsdNotice::StageEditTargetChanged& notice) +{ + if (in_compute) { + MGlobal::executeTaskOnIdle(copyTargetLayerOnIdle, this); + return; + } + + const auto stage = notice.GetStage(); + if (!stage) + return; + + // Note: copying the target layer into an attribute when the edit target + // changes can cause DG evaluation loops because the stage computation + // sets the edit target. + // + // Defer saving the edit target to be done later, on idle. + // + // One symptom of creating a compute loop is that the unit test named + // 'testMayaUsdProxyAccessor' fails because computation did not finish. + copyTargetLayerToAttribute(*stage, *this); +} + void MayaUsdProxyShapeBase::_OnStageObjectsChanged(const UsdNotice::ObjectsChanged& notice) { MProfilingScope profilingScope( diff --git a/lib/mayaUsd/nodes/proxyShapeBase.h b/lib/mayaUsd/nodes/proxyShapeBase.h index 548212268f..6850ec019f 100644 --- a/lib/mayaUsd/nodes/proxyShapeBase.h +++ b/lib/mayaUsd/nodes/proxyShapeBase.h @@ -390,6 +390,7 @@ class MayaUsdProxyShapeBase void _OnStageContentsChanged(const UsdNotice::StageContentsChanged& notice); void _OnStageObjectsChanged(const UsdNotice::ObjectsChanged& notice); void _OnLayerMutingChanged(const UsdNotice::LayerMutingChanged& notice); + void _OnStageEditTargetChanged(const UsdNotice::StageEditTargetChanged& notice); UsdMayaStageNoticeListener _stageNoticeListener; diff --git a/lib/mayaUsd/nodes/proxyShapeStageExtraData.cpp b/lib/mayaUsd/nodes/proxyShapeStageExtraData.cpp index 19bb4ae3f5..23b0ef4db7 100644 --- a/lib/mayaUsd/nodes/proxyShapeStageExtraData.cpp +++ b/lib/mayaUsd/nodes/proxyShapeStageExtraData.cpp @@ -16,6 +16,7 @@ #include "proxyShapeStageExtraData.h" #include +#include #include @@ -62,6 +63,11 @@ void saveTrackedLoadRules(const UsdStageRefPtr& stage) saveTrackedData(stage, copyLoadRulesToAttribute); } +void saveTrackedTargetLayer(const UsdStageRefPtr& stage) +{ + saveTrackedData(stage, copyTargetLayerToAttribute); +} + } // namespace /* static */ @@ -101,7 +107,11 @@ void MayaUsdProxyShapeStageExtraData::removeProxyShape(MayaUsdProxyShapeBase& pr } /* static */ -void MayaUsdProxyShapeStageExtraData::saveAllStageData() { saveAllLoadRules(); } +void MayaUsdProxyShapeStageExtraData::saveAllStageData() +{ + saveAllLoadRules(); + saveAllTargetLayers(); +} /* static */ void MayaUsdProxyShapeStageExtraData::saveAllLoadRules() @@ -116,4 +126,17 @@ void MayaUsdProxyShapeStageExtraData::saveLoadRules(const UsdStageRefPtr& stage) saveTrackedLoadRules(stage); } +/* static */ +void MayaUsdProxyShapeStageExtraData::saveAllTargetLayers() +{ + // Note: passing nullptr means save all stages. + saveTrackedTargetLayer(nullptr); +} + +/* static */ +void MayaUsdProxyShapeStageExtraData::saveTargetLayer(const UsdStageRefPtr& stage) +{ + saveTrackedTargetLayer(stage); +} + } // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/nodes/proxyShapeStageExtraData.h b/lib/mayaUsd/nodes/proxyShapeStageExtraData.h index 7e07cdc8ba..d13c9ccd48 100644 --- a/lib/mayaUsd/nodes/proxyShapeStageExtraData.h +++ b/lib/mayaUsd/nodes/proxyShapeStageExtraData.h @@ -34,7 +34,7 @@ namespace MAYAUSD_NS_DEF { /// triggered before a scene is saved to copy the current proxy shape extra data from the stage /// to the proxy shape. /// -/// The extra data saved this way currently are: payload load rules. +/// The extra data saved this way currently are: payload load rules and current target layer. class MayaUsdProxyShapeStageExtraData { @@ -67,6 +67,14 @@ class MayaUsdProxyShapeStageExtraData /// \brief save load rules of the tracked proxy shape corresponding to the given stage. MAYAUSD_CORE_PUBLIC static void saveLoadRules(const UsdStageRefPtr& stage); + + /// \brief save the target layers of tracked proxy shapes. + MAYAUSD_CORE_PUBLIC + static void saveAllTargetLayers(); + + /// \brief save the target layer of the tracked proxy shape corresponding to the given stage. + MAYAUSD_CORE_PUBLIC + static void saveTargetLayer(const UsdStageRefPtr& stage); }; } // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/python/wrapUtil.cpp b/lib/mayaUsd/python/wrapUtil.cpp index fe613cd937..bc5a284b3f 100644 --- a/lib/mayaUsd/python/wrapUtil.cpp +++ b/lib/mayaUsd/python/wrapUtil.cpp @@ -55,5 +55,6 @@ void wrapUtil() .def("getDictionaryFromEncodedOptions", getDictionaryFromEncodedOptions) .def( "getPathRelativeToMayaSceneFile", - &UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile); + UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile) + .staticmethod("getPathRelativeToMayaSceneFile"); } diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp index 8c578dbb30..23cd2f9443 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp @@ -385,8 +385,55 @@ ShaderPtr GlslFragmentGenerator::generate( true); } - // Add all function calls + // Add all function calls (varies greatly by MaterialX version) +#if MX_COMBINED_VERSION >= 13805 + // Surface shaders need special handling. + if (graph.hasClassification( + ShaderNode::Classification::SHADER | ShaderNode::Classification::SURFACE)) { + // Emit all texturing nodes. These are inputs to any + // closure/shader nodes and need to be emitted first. + emitFunctionCalls(graph, context, pixelStage, ShaderNode::Classification::TEXTURE); + + // Emit function calls for "root" closure/shader nodes. + // These will internally emit function calls for any dependent closure nodes upstream. + for (ShaderGraphOutputSocket* socket : graph.getOutputSockets()) { + if (socket->getConnection()) { + const ShaderNode* upstream = socket->getConnection()->getNode(); + if (upstream->getParent() == &graph + && (upstream->hasClassification(ShaderNode::Classification::CLOSURE) + || upstream->hasClassification(ShaderNode::Classification::SHADER))) { + emitFunctionCall(*upstream, context, pixelStage); + } + } + } + } else { + // No surface shader graph so just generate all + // function calls in order. + emitFunctionCalls(graph, context, pixelStage); + } +#elif MX_COMBINED_VERSION >= 13803 + // Surface shaders need special handling. + if (graph.hasClassification( + ShaderNode::Classification::SHADER | ShaderNode::Classification::SURFACE)) { + // Emit all texturing nodes. These are inputs to any + // closure/shader nodes and need to be emitted first. + emitFunctionCalls(graph, context, pixelStage, ShaderNode::Classification::TEXTURE); + + // Emit function calls for all surface shader nodes. + // These will internally emit their closure function calls. + emitFunctionCalls( + graph, + context, + pixelStage, + ShaderNode::Classification::SHADER | ShaderNode::Classification::SURFACE); + } else { + // No surface shader graph so just generate all + // function calls in order. + emitFunctionCalls(graph, context, pixelStage); + } +#else emitFunctionCalls(graph, context, pixelStage); +#endif // Emit final result // diff --git a/lib/mayaUsd/render/vp2RenderDelegate/draw_item.h b/lib/mayaUsd/render/vp2RenderDelegate/draw_item.h index 38d011cd9c..1a708addf1 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/draw_item.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/draw_item.h @@ -125,6 +125,16 @@ class HdVP2DrawItem final : public HdDrawItem kSelectionHighlight = kRegular << 1 //!< Selection highlight. }; + //! Flags specifying mod properties + enum ModFlags + { + kHideOnPlayback = 1 << 0, + kUnselectable = 1 << 1, + + kNumModFlags = 2, + kModFlagsBitsetSize = 1 << kNumModFlags + }; + public: HdVP2DrawItem(HdVP2RenderDelegate* delegate, const HdRprimSharedData* sharedData); @@ -202,13 +212,21 @@ class HdVP2DrawItem final : public HdDrawItem */ HdVP2DrawItem* GetMod() { return _mod.get(); } - /*! \brief Mark this draw item as being a mod for instances with hide-on-playback enabled. + /*! \brief Marks this draw item as being a mod for instances with particular mod flags. + */ + void SetModFlags(int modFlags) { _modFlags = modFlags; } + + /*! \brief Returns mod flags for this mod. + */ + int GetModFlags() const { return _modFlags; } + + /*! \brief Marks this mod as disabled/enabled. */ - void SetModFlagHideOnPlayback(bool prop) { _modFlagHideOnPlayback = prop; } + void SetModDisabled(bool flag) { _modDisabled = flag; } - /*! \brief Verify if this draw item is a mod for instances with hide-on-playback enabled. + /*! \brief Verifies if this mod is disabled. */ - bool GetModFlagHideOnPlayback() const { return _modFlagHideOnPlayback; } + bool GetModDisabled() const { return _modDisabled; } private: /* @@ -234,8 +252,13 @@ class HdVP2DrawItem final : public HdDrawItem */ std::unique_ptr _mod; - //! flag marking the given draw item as a mod for instances with hide-on-playback enabled. - bool _modFlagHideOnPlayback = false; + /* + _modFlags equal to zero specifies the main draw item, otherwise it stores a combination + of flags defining the drawing characteristics of this mod + */ + int _modFlags = 0; + //! Defines if this mod is disabled (due to absense of instances that require it) + bool _modDisabled = false; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp index 47f40620b4..cb9a2c8896 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp @@ -245,8 +245,9 @@ TF_DEFINE_PRIVATE_TOKENS( (USD_Mtlx_VP2_Material) (NG_Maya) - (image) - (tiledimage) + (ND_surface) + (ND_standard_surface_surfaceshader) + (filename) (i_geomprop_) (geomprop) (uaddressmode) @@ -918,6 +919,16 @@ void _AddMissingTangents(mx::DocumentPtr& mtlxDoc) } } +bool _MxHasFilenameInput(const mx::NodeDefPtr nodeDef) +{ + for (const auto& input : nodeDef->getActiveInputs()) { + if (input->getType() == _mtlxTokens->filename.GetString()) { + return true; + } + } + return false; +} + #endif // WANT_MATERIALX_BUILD #if PXR_VERSION <= 2211 @@ -957,10 +968,30 @@ bool _IsUsdUVTexture(const HdMaterialNode& node) if (_IsMaterialX(node)) { mx::NodeDefPtr nodeDef = _GetMaterialXData()._mtlxLibrary->getNodeDef(node.identifier.GetString()); - if (nodeDef - && (nodeDef->getNodeString() == _mtlxTokens->image.GetString() - || nodeDef->getNodeString() == _mtlxTokens->tiledimage.GetString())) { - return true; + return nodeDef && _MxHasFilenameInput(nodeDef); + } +#endif + + return false; +} + +bool _IsTextureFilenameAttribute(const HdMaterialNode& node, const TfToken& token) +{ + + if (node.identifier.GetString().rfind(UsdImagingTokens->UsdUVTexture.GetString(), 0) == 0 + && token == _tokens->file) { + return true; + } + +#ifdef WANT_MATERIALX_BUILD + if (_IsMaterialX(node)) { + mx::NodeDefPtr nodeDef + = _GetMaterialXData()._mtlxLibrary->getNodeDef(node.identifier.GetString()); + if (nodeDef) { + const auto input = nodeDef->getActiveInput(token.GetString()); + if (input && input->getType() == _mtlxTokens->filename.GetString()) { + return true; + } } } #endif @@ -1860,6 +1891,16 @@ void ConvertNetworkMapToUntextured(HdMaterialNetworkMap& networkMap) auto eraseBegin = std::remove_if(network.nodes.begin(), network.nodes.end(), isInputNode); network.nodes.erase(eraseBegin, network.nodes.end()); network.relationships.clear(); +#ifdef WANT_MATERIALX_BUILD + // Raw MaterialX surface constructor node does not render. Replace with default + // standard_surface: + for (auto& node : network.nodes) { + if (node.identifier == _mtlxTokens->ND_surface) { + node.identifier = _mtlxTokens->ND_standard_surface_surfaceshader; + node.parameters.clear(); + } + } +#endif } } @@ -2392,12 +2433,18 @@ TfToken _RequiresColorManagement( return {}; } - const std::string& upstreamCategory = upstreamDef->getNodeString(); - if (upstreamCategory != _mtlxTokens->image.GetString() - && upstreamCategory != _mtlxTokens->tiledimage.GetString()) { - // upstream is not an image + if (!_MxHasFilenameInput(upstreamDef)) { + // upstream is not a texture return {}; } + + std::vector fileInputs; + for (const auto& input : upstreamDef->getActiveInputs()) { + if (input->getType() == _mtlxTokens->filename.GetString()) { + fileInputs.push_back(TfToken(input->getName())); + } + } + mx::OutputPtr colorOutput = upstreamDef->getActiveOutput(_mtlxTokens->out.GetString()); if (!colorOutput) { return {}; @@ -2409,17 +2456,22 @@ TfToken _RequiresColorManagement( return {}; } - auto itFileParam = upstream.parameters.find(_tokens->file); - if (itFileParam == upstream.parameters.end() - || !itFileParam->second.IsHolding()) { - // No file name to check: - return {}; + SdfAssetPath filenameVal; + for (const auto& inputName : fileInputs) { + auto itFileParam = upstream.parameters.find(inputName); + if (itFileParam != upstream.parameters.end() + && itFileParam->second.IsHolding()) { + filenameVal = itFileParam->second.Get(); + break; + } } - const SdfAssetPath& val = itFileParam->second.Get(); - const std::string& resolvedPath = val.GetResolvedPath(); - const std::string& assetPath = val.GetAssetPath(); - MString colorRuleCmd; + const std::string& resolvedPath = filenameVal.GetResolvedPath(); + if (resolvedPath.empty()) { + return {}; + } + const std::string& assetPath = filenameVal.GetAssetPath(); + MString colorRuleCmd; colorRuleCmd.format( "colorManagementFileRules -evaluate \"^1s\";", (!resolvedPath.empty() ? resolvedPath : assetPath).c_str()); @@ -3068,7 +3120,7 @@ void HdVP2Material::CompiledNetwork::_UpdateShaderInstance( const SdfAssetPath& val = value.UncheckedGet(); const std::string& resolvedPath = val.GetResolvedPath(); const std::string& assetPath = val.GetAssetPath(); - if (_IsUsdUVTexture(node) && token == _tokens->file) { + if (_IsTextureFilenameAttribute(node, token)) { const HdVP2TextureInfo& info = _owner->_AcquireTexture( sceneDelegate, !resolvedPath.empty() ? resolvedPath : assetPath, node); diff --git a/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp b/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp index 0542c912a8..63753000ff 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp @@ -339,9 +339,11 @@ HdReprSharedPtr MayaUsdRPrim::_InitReprCommon( // Instanced primitives with instances in display layers use 'forced' representations to // draw those specific instances, so the 'forced' representations should be inited alongside - if (reprToken != HdVP2ReprTokens->forcedBbox) { + if (reprToken != HdVP2ReprTokens->forcedBbox && reprToken != HdVP2ReprTokens->forcedWire) { refThis.InitRepr( drawScene.GetUsdImagingDelegate(), HdVP2ReprTokens->forcedBbox, dirtyBits); + refThis.InitRepr( + drawScene.GetUsdImagingDelegate(), HdVP2ReprTokens->forcedWire, dirtyBits); } } else { // Sync display layer modes for non-instanced prims. @@ -769,8 +771,8 @@ void MayaUsdRPrim::_SyncDisplayLayerModesInstanced(SdfPath const& id, unsigned i _displayLayerModesInstancedFrame = drawScene.GetFrameCounter(); - _needForcedBBox = false; - _needHideOnPlaybackMod = false; + _forcedReprFlags = 0; + _requiredModFlagsBitset.reset(); if (drawScene.SupportPerInstanceDisplayLayers(id)) { _displayLayerModesInstanced.resize(instanceCount); for (unsigned int usdInstanceId = 0; usdInstanceId < instanceCount; usdInstanceId++) { @@ -779,12 +781,19 @@ void MayaUsdRPrim::_SyncDisplayLayerModesInstanced(SdfPath const& id, unsigned i _PopulateDisplayLayerModes(usdPath, displayLayerModes, drawScene); if (displayLayerModes._reprOverride == kBBox) { - _needForcedBBox = true; + _forcedReprFlags |= kForcedBBox; + } else if (displayLayerModes._reprOverride == kWire) { + _forcedReprFlags |= kForcedWire; } + int requiredModFlags = 0; if (displayLayerModes._hideOnPlayback) { - _needHideOnPlaybackMod = true; + requiredModFlags |= HdVP2DrawItem::kHideOnPlayback; + } + if (displayLayerModes._displayType != kNormal) { + requiredModFlags |= HdVP2DrawItem::kUnselectable; } + _requiredModFlagsBitset.set(requiredModFlags); } } else { _displayLayerModesInstanced.clear(); @@ -1011,34 +1020,71 @@ bool MayaUsdRPrim::_GetMaterialPrimvars( return true; } -bool MayaUsdRPrim::_ShouldSkipInstance( - unsigned int usdInstanceId, - const TfToken& reprToken, - bool hideOnPlaybackItem) const +bool MayaUsdRPrim::_FilterInstanceByDisplayLayer( + unsigned int usdInstanceId, + BasicWireframeColors& instanceColor, + const TfToken& reprToken, + int modFlags, + bool isHighlightItem, + bool isDedicatedHighlightItem) const { if (_displayLayerModesInstanced.size() <= usdInstanceId) { return false; } + // Verify display layer visibility const auto& displayLayerModes = _displayLayerModesInstanced[usdInstanceId]; if (!displayLayerModes._visibility) { return true; } - if (reprToken == HdVP2ReprTokens->forcedBbox) { - if (displayLayerModes._reprOverride != kBBox) { - return true; - } - } else { - if (displayLayerModes._reprOverride == kBBox) { - return true; - } + // Match item's bbox mode against instance's bbox mode + const bool forcedBboxItem = (reprToken == HdVP2ReprTokens->forcedBbox); + const bool overrideBboxInstance = displayLayerModes._reprOverride == kBBox; + if (forcedBboxItem != overrideBboxInstance) { + return true; } + // Match item's wire mode against instance's wire mode + const bool forcedWireItem = (reprToken == HdVP2ReprTokens->forcedWire); + const bool overrideWireInstance = displayLayerModes._reprOverride == kWire; + if (forcedWireItem != overrideWireInstance) { + return true; + } + + // Match item's hide-on-playback mode against that of the instance + const bool hideOnPlaybackItem = (modFlags & HdVP2DrawItem::kHideOnPlayback); if (displayLayerModes._hideOnPlayback != hideOnPlaybackItem) { return true; } + // Match item's 'unselectable' mode against that of the instance + const bool unselectableItem = (modFlags & HdVP2DrawItem::kUnselectable); + const bool unselectableInstance = displayLayerModes._displayType != kNormal; + if (unselectableInstance != unselectableItem) { + return true; + } + + // Template and reference modes may affect visibility and wireframe color of items + if (displayLayerModes._displayType == kTemplate) { + if (!isHighlightItem) { + return true; // Solid geometry is not drawn in the template mode + } else { + instanceColor = (instanceColor == kDormant) ? kTemplateDormat : kTemplateActive; + } + } else if (displayLayerModes._displayType == kReference) { + if (instanceColor == kDormant) { + if (isDedicatedHighlightItem) { + // Hide dedicated highlight items when unselected. Since 'template' and + // 'reference' modes share the same mod, we have to keep dedicated highlight + // item generally enabled, and thus we have a special case here + return true; + } else { + instanceColor = kReferenceDormat; + } + } + } + return false; } @@ -1072,11 +1118,17 @@ void MayaUsdRPrim::_SyncForcedReprs( } }; - if (_needForcedBBox) { + if (_forcedReprFlags & kForcedBBox) { refThis.Sync(delegate, renderParam, dirtyBits, HdVP2ReprTokens->forcedBbox); } else { _ForEachRenderItemInRepr(_FindRepr(reprs, HdVP2ReprTokens->forcedBbox), hideDrawItem); } + + if (_forcedReprFlags & kForcedWire) { + refThis.Sync(delegate, renderParam, dirtyBits, HdVP2ReprTokens->forcedWire); + } else { + _ForEachRenderItemInRepr(_FindRepr(reprs, HdVP2ReprTokens->forcedWire), hideDrawItem); + } } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.h b/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.h index 6113105fcf..4eab6f79df 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.h @@ -186,6 +186,12 @@ class MayaUsdRPrim kWire = 2 }; + enum ForcedReprFlags + { + kForcedBBox = 1 << 0, + kForcedWire = 1 << 1 + }; + struct DisplayLayerModes { //! Requested display layer visibility @@ -213,6 +219,18 @@ class MayaUsdRPrim MColor _wireframeColorRGBA; }; + enum BasicWireframeColors : unsigned char + { + kDormant = 0, + kActive = 1, + kLead = 2, + kTemplateDormat = 3, + kTemplateActive = 4, + kReferenceDormat = 5, + + kInvalid = 255 + }; + static void _ProcessDisplayLayerModes(const MObject& displayLayerObj, DisplayLayerModes& displayLayerModes); @@ -268,10 +286,13 @@ class MayaUsdRPrim void _SyncDisplayLayerModes(SdfPath const& id); void _SyncDisplayLayerModesInstanced(SdfPath const& id, unsigned int instanceCount); - bool _ShouldSkipInstance( - unsigned int usdInstanceId, - const TfToken& reprToken, - bool hideOnPlaybackItem) const; + bool _FilterInstanceByDisplayLayer( + unsigned int usdInstanceId, + BasicWireframeColors& instanceColor, + const TfToken& reprToken, + int modFlags, + bool isHighlightItem, + bool isDedicatedHighlightItem) const; void _SyncForcedReprs( HdRprim& refThis, @@ -349,11 +370,11 @@ class MayaUsdRPrim uint64_t _displayLayerModesFrame { 0 }; uint64_t _displayLayerModesInstancedFrame { 0 }; - // For instanced primitives, specifies if at least one istance has to be hidden on playback - bool _needHideOnPlaybackMod = false; + // For instanced primitives, specifies which mods are required + std::bitset _requiredModFlagsBitset; // forced representations runtime state - bool _needForcedBBox = false; + int _forcedReprFlags { 0 }; uint64_t _forcedReprsFrame { 0 }; //! HideOnPlayback status of the Rprim diff --git a/lib/mayaUsd/render/vp2RenderDelegate/mesh.cpp b/lib/mayaUsd/render/vp2RenderDelegate/mesh.cpp index d2958090d0..b141101efb 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/mesh.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/mesh.cpp @@ -1263,13 +1263,19 @@ void HdVP2Mesh::_AddNewRenderItem( drawItem->SetUsage(HdVP2DrawItem::kSelectionHighlight); } // The item is used for wireframe display and selection highlight. - else if (reprToken == HdReprTokens->wire) { + else if (reprToken == HdReprTokens->wire || reprToken == HdVP2ReprTokens->forcedWire) { renderItem = _CreateWireframeRenderItem( renderItemName, kOpaqueBlue, MSelectionMask::kSelectMeshes, MHWRender::MFrameContext::kExcludeMeshes); drawItem->AddUsage(HdVP2DrawItem::kSelectionHighlight); + if (reprToken == HdVP2ReprTokens->forcedWire) { + renderItem->enable(false); + constexpr int sDrawModeAllButBBox + = MHWRender::MGeometry::kAll & ~MHWRender::MGeometry::kBoundingBox; + renderItem->setDrawMode((MHWRender::MGeometry::DrawMode)sDrawModeAllButBBox); + } } // The item is used for bbox display and selection highlight. else if (reprToken == HdVP2ReprTokens->bbox || reprToken == HdVP2ReprTokens->forcedBbox) { @@ -1457,38 +1463,52 @@ void HdVP2Mesh::_UpdateMods( return; } - HdVP2DrawItem* mod = drawItem->GetMod(); - if (_needHideOnPlaybackMod) { - // Create the mod if needed - if (!mod) { - drawItem->SetMod(std::make_unique(_delegate, &_sharedData)); - mod = drawItem->GetMod(); + // First, collect all existing mods in a bitset. + std::bitset _existingModsBitset; + HdVP2DrawItem* lastNode = drawItem; + for (auto mod = drawItem; mod; mod = mod->GetMod()) { + _existingModsBitset.set(mod->GetModFlags()); + lastNode = mod; + } + + // Then, create missing mods. Skip 0 because it's the main draw item, which always exists + for (int bitsetIndex = 1; bitsetIndex < HdVP2DrawItem::kModFlagsBitsetSize; ++bitsetIndex) { + if (_requiredModFlagsBitset.test(bitsetIndex) && !_existingModsBitset.test(bitsetIndex)) { + auto newMod = std::make_unique(_delegate, &_sharedData); + newMod->SetModFlags(bitsetIndex); // Add render items to the mod in the same way it is done for the original draw item - _AddNewRenderItem(mod, desc, reprToken, subSceneContainer, false); + _AddNewRenderItem(newMod.get(), desc, reprToken, subSceneContainer, false); if (desc.geomStyle == HdMeshGeomStyleHull) { - if (mod->GetRenderItems().size() == 0) { - _CreateSmoothHullRenderItems(*mod, reprToken, *subSceneContainer); + if (newMod->GetRenderItems().size() == 0) { + _CreateSmoothHullRenderItems(*newMod, reprToken, *subSceneContainer); } } - // Make sure to enable hide-on-playback on the mod - mod->SetModFlagHideOnPlayback(true); + // Set this mod's specific properties + if (bitsetIndex & HdVP2DrawItem::kHideOnPlayback) { #ifdef MAYA_HAS_RENDER_ITEM_HIDE_ON_PLAYBACK_API - for (auto& renderItemData : mod->GetRenderItems()) { - renderItemData._renderItem->setHideOnPlayback(true); - } + for (auto& renderItemData : newMod->GetRenderItems()) { + renderItemData._renderItem->setHideOnPlayback(true); + } #endif - } + } - // Update the mod - for (auto& renderItemData : mod->GetRenderItems()) { - _UpdateDrawItem(sceneDelegate, mod, renderItemData, desc, reprToken); + lastNode->SetMod(std::move(newMod)); + lastNode = lastNode->GetMod(); } - } else if (mod) { - // Disable the mod - for (auto& renderItemData : mod->GetRenderItems()) { - if (renderItemData._enabled) { + } + + // Finally, update/disable mods + for (auto mod = drawItem->GetMod(); mod; mod = mod->GetMod()) { + if (_requiredModFlagsBitset.test(mod->GetModFlags())) { + mod->SetModDisabled(false); + for (auto& renderItemData : mod->GetRenderItems()) { + _UpdateDrawItem(sceneDelegate, mod, renderItemData, desc, reprToken); + } + } else if (!mod->GetModDisabled()) { + mod->SetModDisabled(true); + for (auto& renderItemData : mod->GetRenderItems()) { renderItemData._enabled = false; _delegate->GetVP2ResourceRegistry().EnqueueCommit( [&renderItemData]() { renderItemData._renderItem->enable(false); }); @@ -1538,7 +1558,9 @@ void HdVP2Mesh::_UpdateDrawItem( const bool isHighlightItem = drawItem->ContainsUsage(HdVP2DrawItem::kSelectionHighlight); const bool inTemplateMode = _displayLayerModes._displayType == MayaUsdRPrim::kTemplate; const bool inReferenceMode = _displayLayerModes._displayType == MayaUsdRPrim::kReference; - const bool inPureSelectionHighlightMode = isDedicatedHighlightItem && !inTemplateMode; + const bool unselectableItem = (drawItem->GetModFlags() & HdVP2DrawItem::kUnselectable); + const bool inPureSelectionHighlightMode + = isDedicatedHighlightItem && !inTemplateMode && !unselectableItem; // We don't need to update the selection-highlight-only item when there is no selection // highlight change and the mesh is not selected. Render item stores its own @@ -1792,49 +1814,44 @@ void HdVP2Mesh::_UpdateDrawItem( // render item, and which colors should be used to draw those instances. // Store info per instance - const unsigned char dormant = 0; - const unsigned char active = 1; - const unsigned char lead = 2; - const unsigned char invalid = 255; - - std::vector instanceInfo; + std::vector instanceInfo; // depending on the type of render item we want to set different values // into instanceInfo; - unsigned char modeDormant = invalid; - unsigned char modeActive = invalid; - unsigned char modeLead = invalid; + BasicWireframeColors modeDormant = kInvalid; + BasicWireframeColors modeActive = kInvalid; + BasicWireframeColors modeLead = kInvalid; if (!isHighlightItem) { stateToCommit._instanceColorParam = kDiffuseColorStr; if (!usingShadedSelectedInstanceItem) { if (isShadedSelectedInstanceItem) { - modeDormant = invalid; - modeActive = invalid; - modeLead = invalid; + modeDormant = kInvalid; + modeActive = kInvalid; + modeLead = kInvalid; } else { - modeDormant = active; - modeActive = active; - modeLead = active; + modeDormant = kActive; + modeActive = kActive; + modeLead = kActive; } } else { if (isShadedSelectedInstanceItem) { - modeDormant = invalid; - modeActive = active; - modeLead = active; + modeDormant = kInvalid; + modeActive = kActive; + modeLead = kActive; } else { - modeDormant = active; - modeActive = invalid; - modeLead = invalid; + modeDormant = kActive; + modeActive = kInvalid; + modeLead = kInvalid; } } } else if (_selectionStatus == kFullyLead || _selectionStatus == kFullyActive) { - modeDormant = _selectionStatus == kFullyLead ? lead : active; + modeDormant = _selectionStatus == kFullyLead ? kLead : kActive; stateToCommit._instanceColorParam = kSolidColorStr; } else { - modeDormant = inPureSelectionHighlightMode ? invalid : dormant; - modeActive = active; - modeLead = lead; + modeDormant = inPureSelectionHighlightMode ? kInvalid : kDormant; + modeActive = kActive; + modeLead = kLead; stateToCommit._instanceColorParam = kSolidColorStr; } @@ -1884,7 +1901,10 @@ void HdVP2Mesh::_UpdateDrawItem( const MColor wireframeColors[] = { drawScene.GetWireframeColor(), drawScene.GetSelectionHighlightColor(HdPrimTypeTokens->mesh), - drawScene.GetSelectionHighlightColor() }; + drawScene.GetSelectionHighlightColor(), + drawScene.GetTemplateColor(false), + drawScene.GetTemplateColor(true), + drawScene.GetReferenceColor() }; bool useWireframeColors = stateToCommit._instanceColorParam == kSolidColorStr; MFloatArray* shadedColors = nullptr; @@ -1926,17 +1946,23 @@ void HdVP2Mesh::_UpdateDrawItem( #endif _SyncDisplayLayerModesInstanced(id, instanceCount); - const bool hideOnPlaybackItem = drawItem->GetModFlagHideOnPlayback(); + const int modFlags = drawItem->GetModFlags(); stateToCommit._instanceTransforms = std::make_shared(); stateToCommit._instanceColors = std::make_shared(); for (unsigned int usdInstanceId = 0; usdInstanceId < instanceCount; usdInstanceId++) { - unsigned char info = instanceInfo[usdInstanceId]; - if (info == invalid) + auto info = instanceInfo[usdInstanceId]; + if (info == kInvalid) continue; // Check display layer modes of this instance - if (_ShouldSkipInstance(usdInstanceId, reprToken, hideOnPlaybackItem)) + if (_FilterInstanceByDisplayLayer( + usdInstanceId, + info, + reprToken, + modFlags, + isHighlightItem, + isDedicatedHighlightItem)) continue; #ifndef MAYA_UPDATE_UFE_IDENTIFIER_SUPPORT @@ -2107,8 +2133,9 @@ void HdVP2Mesh::_UpdateDrawItem( } } #endif - // In template and reference modes, items should have no selection - if (inTemplateMode || inReferenceMode) { + // In template and reference modes, items should have no selection. + // Unselectable mods have no selection as well. + if (inTemplateMode || inReferenceMode || unselectableItem) { selectionMask = MSelectionMask(); } diff --git a/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp b/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp index eeef13557b..9fe348cd9d 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp @@ -279,6 +279,13 @@ void _ConfigureReprs() /*flatShadingEnabled=*/false, /*blendWireframeColor=*/false); + const HdMeshReprDesc reprDescWire( + HdMeshGeomStyleHullEdgeOnly, + HdCullStyleDontCare, + HdMeshReprDescTokens->surfaceShader, + /*flatShadingEnabled=*/false, + /*blendWireframeColor=*/true); + // Hull desc for shaded display, edge desc for selection highlight. HdMesh::ConfigureRepr(HdReprTokens->smoothHull, reprDescHull, reprDescEdge); HdMesh::ConfigureRepr(HdVP2ReprTokens->smoothHullUntextured, reprDescHull, reprDescEdge); @@ -294,6 +301,7 @@ void _ConfigureReprs() // Forced representations are used for instanced geometry with display layer overrides HdMesh::ConfigureRepr(HdVP2ReprTokens->forcedBbox, reprDescEdge); + HdMesh::ConfigureRepr(HdVP2ReprTokens->forcedWire, reprDescWire); // smooth hull for untextured display HdBasisCurves::ConfigureRepr( diff --git a/lib/mayaUsd/render/vp2RenderDelegate/tokens.h b/lib/mayaUsd/render/vp2RenderDelegate/tokens.h index eba7d4ea73..41a697a001 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/tokens.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/tokens.h @@ -26,7 +26,8 @@ PXR_NAMESPACE_OPEN_SCOPE (bbox) \ (defaultMaterial) \ (smoothHullUntextured) \ - (forcedBbox) + (forcedBbox) \ + (forcedWire) #define HDVP2_TOKENS \ (displayColorAndOpacity) \ diff --git a/lib/mayaUsd/resources/scripts/CMakeLists.txt b/lib/mayaUsd/resources/scripts/CMakeLists.txt index 810d3d2e59..f015db2837 100644 --- a/lib/mayaUsd/resources/scripts/CMakeLists.txt +++ b/lib/mayaUsd/resources/scripts/CMakeLists.txt @@ -3,6 +3,7 @@ list(APPEND scripts_src mayaUsdLibRegisterStrings.py mayaUsdAddMayaReference.mel mayaUsdAddMayaReference.py + mayaUsdAddUSDReference.mel mayaUsdCacheMayaReference.mel mayaUsdCacheMayaReference.py mayaUsdDuplicateAsMayaDataOptions.mel diff --git a/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel b/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel new file mode 100644 index 0000000000..a260d81219 --- /dev/null +++ b/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel @@ -0,0 +1,36 @@ +// Copyright 2022 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. +// + +// Unfortunate MEL to Python shims because of fileDialog2 requirements that +// optionsUICreate, optionsUIInit and optionsUICommit2 arguments be MEL +// procedures. + +global proc addUSDReferenceCreateUi(string $parent) +{ + setParent $parent; + string $layout = `scrollLayout -childResizable true`; + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiCreate(r'''" + $layout + "''')"); +} + +global proc addUSDReferenceInitUi(string $parent, string $filterType) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiInit(r'''" + $parent + "''', r'''" + $filterType + "''')"); +} + +global proc addUSDReferenceToUsdCommitUi(string $parent, string $selectedFile) +{ + setParent $parent; + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiCommit(r'''" + $parent + "''', r'''" + $selectedFile + "''')"); +} diff --git a/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp b/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp index 5d7fc68a19..eeaec812e0 100644 --- a/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp +++ b/lib/mayaUsd/sceneIndex/proxyShapeSceneIndexPlugin.cpp @@ -18,6 +18,7 @@ #if PXR_VERSION >= 2211 +#include #include #include #include @@ -60,7 +61,10 @@ HdSceneIndexBaseRefPtr MayaUsdProxyShapeMayaNodeSceneIndexPlugin::_AppendSceneIn auto proxyShape = dynamic_cast(dependNodeFn.userNode()); if (TF_VERIFY(proxyShape, "Error getting MayaUsdProxyShapeBase")) { - return MayaUsd::MayaUsdProxyShapeSceneIndex::New(proxyShape); + auto psSceneIndex = MayaUsd::MayaUsdProxyShapeSceneIndex::New(proxyShape); + // Flatten transforms, visibility, purpose, model, and material + // bindings over hierarchies. + return HdFlatteningSceneIndex::New(psSceneIndex); } } diff --git a/lib/mayaUsd/ufe/StagesSubject.cpp b/lib/mayaUsd/ufe/StagesSubject.cpp index 9073e3f066..1b3b0d82ce 100644 --- a/lib/mayaUsd/ufe/StagesSubject.cpp +++ b/lib/mayaUsd/ufe/StagesSubject.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #ifdef UFE_V2_FEATURES_AVAILABLE @@ -284,6 +285,25 @@ void attributeMetadataChanged( sendAttributeMetadataChanged(ufePath, changedToken, changeType, metadataKeys); } } + +std::vector getMetadataKeys(const std::string& strMetadata) +{ + std::vector metadataKeys; + const std::regex rgx("(')([^ ]*)(':)"); + std::string metadataKey; + + for (std::sregex_iterator it(strMetadata.begin(), strMetadata.end(), rgx), it_end; it != it_end; + ++it) { + if (it->empty()) { + continue; + } + metadataKey = std::string((*it)[0]); + metadataKey = metadataKey.substr(metadataKey.find('\'') + 1, metadataKey.rfind('\'') - 1); + metadataKeys.push_back(metadataKey); + } + + return metadataKeys; +} #endif void processAttributeChanges( @@ -321,12 +341,14 @@ void processAttributeChanges( sendMetadataChanged = true; std::stringstream newMetadataStream; newMetadataStream << infoChanged.second.second; - std::string newMetadataKey = newMetadataStream.str(); - if (!newMetadataKey.empty()) { + const std::string strMetadata = newMetadataStream.str(); + // e.g. strMetadata string format: + // "'uifolder':,'uisoftmin':0.0, 'uihide':1, 'uiorder':0" + if (!strMetadata.empty()) { // Find the modified key which is between a pair of single quotes. - newMetadataKey = newMetadataKey.substr( - newMetadataKey.find('\'') + 1, newMetadataKey.rfind('\'') - 2); - metadataKeys.insert(newMetadataKey); + for (const auto& newMetadataKey : getMetadataKeys(strMetadata)) { + metadataKeys.insert(newMetadataKey); + } } } } diff --git a/lib/mayaUsd/ufe/UsdConnectionHandler.cpp b/lib/mayaUsd/ufe/UsdConnectionHandler.cpp index 4bd215769d..7f5ec77ae6 100644 --- a/lib/mayaUsd/ufe/UsdConnectionHandler.cpp +++ b/lib/mayaUsd/ufe/UsdConnectionHandler.cpp @@ -111,6 +111,7 @@ _GetShaderNodeDef(const PXR_NS::UsdPrim& prim, const PXR_NS::TfToken& attrName) return registry.GetShaderNodeByIdentifier(srcInfoId); } +#if PXR_VERSION < 2302 void _SendStrongConnectionChangeNotification(const UsdPrim& usdPrim) { // See https://github.com/PixarAnimationStudios/USD/issues/2013 for details. @@ -124,6 +125,7 @@ void _SendStrongConnectionChangeNotification(const UsdPrim& usdPrim) usdPrim.GetStage()->DefinePrim(waPath); usdPrim.GetStage()->RemovePrim(waPath); } +#endif } // namespace @@ -246,9 +248,11 @@ bool UsdConnectionHandler::createConnection( } } +#if PXR_VERSION < 2302 if (retVal) { _SendStrongConnectionChangeNotification(dstApi.GetPrim()); } +#endif return retVal; } @@ -295,9 +299,11 @@ bool UsdConnectionHandler::deleteConnection( } } +#if PXR_VERSION < 2302 if (retVal) { _SendStrongConnectionChangeNotification(dstUsdAttr->usdPrim()); } +#endif return retVal; } diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index fe47ee4a06..470e078b9e 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -32,10 +32,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -473,58 +475,105 @@ class ToggleInstanceableStateCommand : public Ufe::UndoableCommand bool _instanceable; }; -const char* selectUSDFileScriptPre = R"mel( -global proc string SelectUSDFileForAddReference() +const std::string getTargetLayerFilePath(const UsdPrim& prim) { - string $result[] = `fileDialog2 - -fileMode 1 - -caption "Add Reference to USD Prim" - -fileFilter "USD Files )mel"; + auto stage = prim.GetStage(); + if (!stage) + return {}; -const char* selectUSDFileScriptPost = R"mel("`; + auto layer = stage->GetEditTarget().GetLayer(); + if (!layer) + return {}; - if (0 == size($result)) - return ""; - else - return $result[0]; + return layer->GetRealPath(); +} + +bool _prepareUSDReferenceTargetLayer(const UsdPrim& prim) +{ + const char* script = "import mayaUsd_USDRootFileRelative as murel\n" + "murel.usdFileRelative.setRelativeFilePathRoot(r'''%s''')"; + + const std::string commandString = TfStringPrintf(script, getTargetLayerFilePath(prim).c_str()); + return MGlobal::executePythonCommand(commandString.c_str()); } -SelectUSDFileForAddReference(); -)mel"; // Ask SDF for all supported extensions: const char* _selectUSDFileScript() { - static std::string commandString; if (commandString.empty()) { // This is an interactive call from the main UI thread. No need for SMP protections. - commandString = selectUSDFileScriptPre; - std::string usdUiString = "("; - std::string usdSelector = ""; - std::string otherUiString = ""; - std::string otherSelector = ""; + // The goal of the following loop is to build a first file filter that allow any + // USD-compatible file format, then a series of file filters, one per particular + // file format. So for N different file formats, we will have N+1 filters. + + std::vector usdUiStrings; + std::vector usdSelectors; + std::vector otherUiStrings; + std::vector otherSelectors; for (auto&& extension : SdfFileFormat::FindAllFileFormatExtensions()) { // Put USD first if (extension.rfind("usd", 0) == 0) { - if (!usdSelector.empty()) { - usdUiString += " "; - } - usdUiString += "*." + extension; - usdSelector += ";;*." + extension; + usdUiStrings.push_back("*." + extension); + usdSelectors.push_back("*." + extension); } else { - otherUiString += " *." + extension; - otherSelector += ";;*." + extension; + otherUiStrings.push_back("*." + extension); + otherSelectors.push_back("*." + extension); } } - commandString += usdUiString + otherUiString + ")" + usdSelector + otherSelector - + selectUSDFileScriptPost; + + usdUiStrings.insert(usdUiStrings.end(), otherUiStrings.begin(), otherUiStrings.end()); + usdSelectors.insert(usdSelectors.end(), otherSelectors.begin(), otherSelectors.end()); + + const char* script = R"mel( + global proc string SelectUSDFileForAddReference() + { + string $result[] = `fileDialog2 + -fileMode 1 + -caption "Add Reference to USD Prim" + -fileFilter "USD Files (%s);;%s" + -optionsUICreate addUSDReferenceCreateUi + -optionsUIInit addUSDReferenceInitUi + -optionsUICommit2 addUSDReferenceToUsdCommitUi`; + + if (0 == size($result)) + return ""; + else + return $result[0]; + } + SelectUSDFileForAddReference(); + )mel"; + + commandString = TfStringPrintf( + script, TfStringJoin(usdUiStrings).c_str(), TfStringJoin(usdSelectors, ";;").c_str()); } + return commandString.c_str(); } +std::string +makeUSDReferenceFilePathRelativeIfRequested(const std::string& filePath, const UsdPrim& prim) +{ + if (!UsdMayaUtilFileSystem::requireUsdPathsRelativeToEditTargetLayer()) + return filePath; + + const std::string layerDirPath = UsdMayaUtilFileSystem::getDir(getTargetLayerFilePath(prim)); + + auto relativePathAndSuccess = UsdMayaUtilFileSystem::makePathRelativeTo(filePath, layerDirPath); + + if (!relativePathAndSuccess.second) { + TF_WARN( + "File name (%s) cannot be resolved as relative to the current edit target layer, " + "using the absolute path.", + filePath.c_str()); + } + + return relativePathAndSuccess.first; +} + const char* clearAllReferencesConfirmScript = R"( global proc string ClearAllUSDReferencesConfirm() { @@ -1210,9 +1259,15 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return nullptr; #endif } else if (itemPath[0] == AddUsdReferenceUndoableCommand::commandName) { + if (!_prepareUSDReferenceTargetLayer(prim())) + return nullptr; + MString fileRef = MGlobal::executeCommandStringResult(_selectUSDFileScript()); + if (fileRef.length() == 0) + return nullptr; - std::string path = UsdMayaUtil::convert(fileRef); + const std::string path + = makeUSDReferenceFilePathRelativeIfRequested(UsdMayaUtil::convert(fileRef), prim()); if (path.empty()) return nullptr; diff --git a/lib/mayaUsd/ufe/UsdMaterial.cpp b/lib/mayaUsd/ufe/UsdMaterial.cpp index 4733ce1db0..84b529ba61 100644 --- a/lib/mayaUsd/ufe/UsdMaterial.cpp +++ b/lib/mayaUsd/ufe/UsdMaterial.cpp @@ -17,36 +17,12 @@ #include -#include #include #include PXR_NAMESPACE_USING_DIRECTIVE -namespace { - -std::vector getConnectedShaders(const PXR_NS::UsdShadeOutput& port) -{ - // Dig down across NodeGraph boundaries until a surface shader is found. - std::vector shaders; - for (const auto& sourceInfo : port.GetConnectedSources()) { - if (sourceInfo.source.GetPrim().GetTypeName() != "Shader") { - if (sourceInfo.sourceType == UsdShadeAttributeType::Output) { - const std::vector connectedShaders - = getConnectedShaders(sourceInfo.source.GetOutput(sourceInfo.sourceName)); - shaders.insert( - std::end(shaders), std::begin(connectedShaders), std::end(connectedShaders)); - } - } else { - shaders.push_back(sourceInfo.source.GetPrim()); - } - } - return shaders; -} - -} // namespace - namespace MAYAUSD_NS_DEF { namespace ufe { @@ -83,11 +59,7 @@ std::vector UsdMaterial::getMaterials() const // 1. Simple case: A material is directly attached to our object. const PXR_NS::UsdShadeMaterial material = directBinding.GetMaterial(); if (material) { - for (const auto& output : material.GetSurfaceOutputs()) { - for (const auto& shader : getConnectedShaders(output)) { - materialPrims.push_back(shader); - } - } + materialPrims.push_back(material.GetPrim()); } // 2. Check whether multiple materials are attached to this object via geometry subsets. @@ -96,20 +68,12 @@ std::vector UsdMaterial::getMaterials() const const UsdShadeMaterial material = subsetBindingAPI.ComputeBoundMaterial(UsdShadeTokens->surface); if (material) { - for (const auto& output : material.GetSurfaceOutputs()) { - for (const auto& shader : getConnectedShaders(output)) { - materialPrims.push_back(shader); - } - } + materialPrims.push_back(material.GetPrim()); } } // 3. Find the associated Ufe::SceneItem for each material attached to our object. - for (const auto& materialPrim : materialPrims) { - if (!materialPrim) { - continue; - } - + for (auto& materialPrim : materialPrims) { const PXR_NS::SdfPath& materialSdfPath = materialPrim.GetPath(); const Ufe::Path materialUfePath = usdPathToUfePathSegment(materialSdfPath); @@ -122,7 +86,7 @@ std::vector UsdMaterial::getMaterials() const continue; const auto ufePath = Ufe::Path({ stagePathSegments[0], materialPathSegments[0] }); - // Now we have the full path to the material's SceneItem + // Now we have the full path to the material's SceneItem. materials.push_back(UsdSceneItem::create(ufePath, materialPrim)); } diff --git a/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.cpp b/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.cpp index 9e4ef6222d..42f16be8e7 100644 --- a/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.cpp +++ b/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.cpp @@ -245,6 +245,8 @@ Ufe::Vector3d UsdTransform3dCommonAPI::rotatePivot() const return toUfe(pvt); } +Ufe::Vector3d UsdTransform3dCommonAPI::scalePivot() const { return rotatePivot(); } + Ufe::SetMatrix4dUndoableCommand::Ptr UsdTransform3dCommonAPI::setMatrixCmd(const Ufe::Matrix4d& m) { if (!isAttributeEditAllowed(prim(), TfToken("xformOp:translate")) diff --git a/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.h b/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.h index 17c86eedfa..242ef2e5cd 100644 --- a/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.h +++ b/lib/mayaUsd/ufe/UsdTransform3dCommonAPI.h @@ -62,6 +62,8 @@ class MAYAUSD_CORE_PUBLIC UsdTransform3dCommonAPI : public UsdTransform3dBase void rotatePivot(double x, double y, double z) override; Ufe::Vector3d rotatePivot() const override; + Ufe::Vector3d scalePivot() const override; + Ufe::SetMatrix4dUndoableCommand::Ptr setMatrixCmd(const Ufe::Matrix4d& m) override; private: diff --git a/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp b/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp index cd252850d6..968f73714c 100644 --- a/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp +++ b/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp @@ -68,20 +68,6 @@ UsdAttribute* usdAttrFromUfeAttr(const Ufe::Attribute::Ptr& attr) return dynamic_cast(attr.get()); } -bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr) -{ - PXR_NS::SdfPathVector connectedAttrs; - dstUsdAttr.GetConnections(&connectedAttrs); - - for (PXR_NS::SdfPath path : connectedAttrs) { - if (path == srcUsdAttr.GetPath()) { - return true; - } - } - - return false; -} - PXR_NS::SdrShaderNodeConstPtr _GetShaderNodeDef(const PXR_NS::UsdPrim& prim, const PXR_NS::TfToken& attrName) { @@ -118,6 +104,7 @@ _GetShaderNodeDef(const PXR_NS::UsdPrim& prim, const PXR_NS::TfToken& attrName) return registry.GetShaderNodeByIdentifier(srcInfoId); } +#if PXR_VERSION < 2302 void _SendStrongConnectionChangeNotification(const UsdPrim& usdPrim) { // See https://github.com/PixarAnimationStudios/USD/issues/2013 for details. @@ -131,6 +118,7 @@ void _SendStrongConnectionChangeNotification(const UsdPrim& usdPrim) usdPrim.GetStage()->DefinePrim(waPath); usdPrim.GetStage()->RemovePrim(waPath); } +#endif } // namespace @@ -169,7 +157,7 @@ void UsdUndoCreateConnectionCommand::execute() return; } - if (isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { + if (MayaUsd::ufe::isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { return; } @@ -243,12 +231,19 @@ void UsdUndoCreateConnectionCommand::execute() } } +#if PXR_VERSION < 2302 if (isConnected) { _SendStrongConnectionChangeNotification(dstApi.GetPrim()); } else { _srcInfo = nullptr; _dstInfo = nullptr; } +#else + if (!isConnected) { + _srcInfo = nullptr; + _dstInfo = nullptr; + } +#endif } Ufe::Connection::Ptr UsdUndoCreateConnectionCommand::connection() const @@ -294,7 +289,7 @@ void UsdUndoDeleteConnectionCommand::execute() UsdAttribute* dstUsdAttr = usdAttrFromUfeAttr(dstAttr); if (!srcUsdAttr || !dstUsdAttr - || !isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { + || !MayaUsd::ufe::isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { return; } @@ -312,25 +307,20 @@ void UsdUndoDeleteConnectionCommand::execute() // Remove attribute if it does not have a value, default value, or time samples. We do this // on Shader nodes and on the Material outputs since they are re-created automatically. // Other NodeGraph inputs and outputs require explicit removal. - if (!dstUsdAttr->usdAttribute().HasValue()) { - UsdShadeShader asShader(dstUsdAttr->usdPrim()); - if (asShader) { - dstUsdAttr->usdPrim().RemoveProperty(dstUsdAttr->usdAttribute().GetName()); - } - UsdShadeMaterial asMaterial(dstUsdAttr->usdPrim()); - if (asMaterial) { - const TfToken baseName = dstUsdAttr->usdAttribute().GetBaseName(); - if (baseName == UsdShadeTokens->surface || baseName == UsdShadeTokens->volume - || baseName == UsdShadeTokens->displacement) { - dstUsdAttr->usdPrim().RemoveProperty(dstUsdAttr->usdAttribute().GetName()); - } - } + if (MayaUsd::ufe::canRemoveDstProperty(dstUsdAttr->usdAttribute())) { + dstUsdAttr->usdPrim().RemoveProperty(dstUsdAttr->usdAttribute().GetName()); + } + + if (MayaUsd::ufe::canRemoveSrcProperty(srcUsdAttr->usdAttribute())) { + srcUsdAttr->usdPrim().RemoveProperty(srcUsdAttr->usdAttribute().GetName()); } } +#if PXR_VERSION < 2302 if (isDisconnected) { _SendStrongConnectionChangeNotification(dstUsdAttr->usdPrim()); } +#endif } void UsdUndoDeleteConnectionCommand::undo() { _undoableItem.undo(); } diff --git a/lib/mayaUsd/ufe/Utils.cpp b/lib/mayaUsd/ufe/Utils.cpp index 43c1b95283..c5a5c48c70 100644 --- a/lib/mayaUsd/ufe/Utils.cpp +++ b/lib/mayaUsd/ufe/Utils.cpp @@ -447,6 +447,120 @@ TfTokenVector getProxyShapePurposes(const Ufe::Path& path) return purposes; } +bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr) +{ + PXR_NS::SdfPathVector connectedAttrs; + dstUsdAttr.GetConnections(&connectedAttrs); + + for (PXR_NS::SdfPath path : connectedAttrs) { + if (path == srcUsdAttr.GetPath()) { + return true; + } + } + + return false; +} + +bool canRemoveSrcProperty(const PXR_NS::UsdAttribute& srcAttr) +{ + + // Do not remove if it has a value. + if (srcAttr.HasValue()) { + return false; + } + + PXR_NS::SdfPathVector connectedAttrs; + srcAttr.GetConnections(&connectedAttrs); + + // Do not remove if it has connections. + if (!connectedAttrs.empty()) { + return false; + } + + const auto prim = srcAttr.GetPrim(); + + if (!prim) { + return false; + } + + PXR_NS::UsdShadeNodeGraph ngPrim(prim); + + if (!ngPrim) { + const auto primParent = prim.GetParent(); + + if (!primParent) { + return false; + } + + // Do not remove if there is a connection with a prim. + for (const auto& childPrim : primParent.GetChildren()) { + if (childPrim != prim) { + for (const auto& attribute : childPrim.GetAttributes()) { + const PXR_NS::UsdAttribute dstUsdAttr = attribute.As(); + if (isConnected(srcAttr, dstUsdAttr)) { + return false; + } + } + } + } + + // Do not remove if there is a connection with the parent prim. + for (const auto& attribute : primParent.GetAttributes()) { + const PXR_NS::UsdAttribute dstUsdAttr = attribute.As(); + if (isConnected(srcAttr, dstUsdAttr)) { + return false; + } + } + + return true; + } + + // Do not remove boundary properties even if there are connections. + return false; +} + +bool canRemoveDstProperty(const PXR_NS::UsdAttribute& dstAttr) +{ + + // Do not remove if it has a value. + if (dstAttr.HasValue()) { + return false; + } + + PXR_NS::SdfPathVector connectedAttrs; + dstAttr.GetConnections(&connectedAttrs); + + // Do not remove if it has connections. + if (!connectedAttrs.empty()) { + return false; + } + + const auto prim = dstAttr.GetPrim(); + + if (!prim) { + return false; + } + + PXR_NS::UsdShadeNodeGraph ngPrim(prim); + + if (!ngPrim) { + return true; + } + + UsdShadeMaterial asMaterial(prim); + if (asMaterial) { + const TfToken baseName = dstAttr.GetBaseName(); + // Remove Material intrinsic outputs since they are re-created automatically. + if (baseName == UsdShadeTokens->surface || baseName == UsdShadeTokens->volume + || baseName == UsdShadeTokens->displacement) { + return true; + } + } + + // Do not remove boundary properties even if there are connections. + return false; +} + namespace { SdfLayerHandle getStrongerLayer( @@ -533,7 +647,14 @@ void applyCommandRestriction( auto primSpec = MayaUsdUtils::getPrimSpecAtEditTarget(prim); auto primStack = prim.GetPrimStack(); std::string layerDisplayName; - std::string message { "It is defined on another layer" }; + + // When the command is forbidden even for the strongest layer, that means + // that the operation is a multi-layers operation and there is no target + // layer that would allow it to proceed. In that case, do not suggest changing + // the target. + std::string message = allowStronger ? "It is defined on another layer. " : ""; + std::string instructions = allowStronger ? "Please set %s as the target layer to proceed." + : "It would orphan opinions on the layer %s."; // iterate over the prim stack, starting at the highest-priority layer. for (const auto& spec : primStack) { @@ -563,7 +684,9 @@ void applyCommandRestriction( // layers ). if (primSpec->GetLayer() != spec->GetLayer()) { layerDisplayName.append("[" + layerName + "]"); - message = "It has a stronger opinion on another layer"; + if (allowStronger) { + message = "It has a stronger opinion on another layer. "; + } break; } continue; @@ -590,12 +713,14 @@ void applyCommandRestriction( if (!layerDisplayName.empty()) { if (allowedInStrongerLayer(prim, primStack, allowStronger)) return; + std::string formattedInstructions + = TfStringPrintf(instructions.c_str(), layerDisplayName.c_str()); std::string err = TfStringPrintf( - "Cannot %s [%s]. %s. Please set %s as the target layer to proceed.", + "Cannot %s [%s]. %s%s", commandName.c_str(), prim.GetName().GetString().c_str(), message.c_str(), - layerDisplayName.c_str()); + formattedInstructions.c_str()); throw std::runtime_error(err.c_str()); } } diff --git a/lib/mayaUsd/ufe/Utils.h b/lib/mayaUsd/ufe/Utils.h index f7a3f50e8d..20c4296ecc 100644 --- a/lib/mayaUsd/ufe/Utils.h +++ b/lib/mayaUsd/ufe/Utils.h @@ -150,6 +150,21 @@ PXR_NS::UsdTimeCode getTime(const Ufe::Path& path); MAYAUSD_CORE_PUBLIC PXR_NS::TfTokenVector getProxyShapePurposes(const Ufe::Path& path); +//! Check if the src and dst attributes are connected. +//! \return True, if they are connected. +MAYAUSD_CORE_PUBLIC +bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr); + +//! Check if a source connection property is allowed to be removed. +//! \return True, if the property can be removed. +MAYAUSD_CORE_PUBLIC +bool canRemoveSrcProperty(const PXR_NS::UsdAttribute& srcAttr); + +//! Check if a destination connection property is allowed to be removed. +//! \return True, if the property can be removed. +MAYAUSD_CORE_PUBLIC +bool canRemoveDstProperty(const PXR_NS::UsdAttribute& dstAttr); + #ifdef UFE_V2_FEATURES_AVAILABLE MAYAUSD_CORE_PUBLIC Ufe::Attribute::Type usdTypeToUfe(const PXR_NS::UsdAttribute& usdAttr); diff --git a/lib/mayaUsd/utils/CMakeLists.txt b/lib/mayaUsd/utils/CMakeLists.txt index e224065f12..4562e9fd7b 100644 --- a/lib/mayaUsd/utils/CMakeLists.txt +++ b/lib/mayaUsd/utils/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources(${PROJECT_NAME} progressBarScope.cpp selectability.cpp stageCache.cpp + targetLayer.cpp traverseLayer.cpp undoHelperCommand.cpp util.cpp @@ -54,6 +55,7 @@ set(HEADERS progressBarScope.h selectability.h stageCache.h + targetLayer.h traverseLayer.h undoHelperCommand.h util.h diff --git a/lib/mayaUsd/utils/diagnosticDelegate.cpp b/lib/mayaUsd/utils/diagnosticDelegate.cpp index d33d81767f..0100cfda0e 100644 --- a/lib/mayaUsd/utils/diagnosticDelegate.cpp +++ b/lib/mayaUsd/utils/diagnosticDelegate.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,7 @@ std::unique_ptr _batchedWarnings; std::unique_ptr _batchedErrors; std::unique_ptr _waker; std::unique_ptr _flusher; +MCallbackId _exitCallback; // The delegate can be installed by multiple plugins (e.g. pxrUsd and // mayaUsdPlugin), so keep track of installations to ensure that we only add @@ -155,7 +157,12 @@ class ErrorOnlyDelegate : public UsdUtilsCoalescingDiagnosticDelegate class DiagnosticFlusher { public: - DiagnosticFlusher() { } + DiagnosticFlusher() + { + if (!IsDiagnosticBatchingEnabled()) { + _maximumUnbatchedDiagnostics = std::numeric_limits::max(); + } + } ~DiagnosticFlusher() { } @@ -378,6 +385,12 @@ class WakeUpDelegate : public TfDiagnosticMgr::Delegate void IssueFatalError(const TfCallContext&, const std::string&) override { } }; +void beforeExitCallback(void* /*clientData*/) +{ + // Make sure the diagnostic messages are flushed when Maya exits. + UsdMayaDiagnosticDelegate::Flush(); +} + } // anonymous namespace void UsdMayaDiagnosticDelegate::InstallDelegate() @@ -405,6 +418,8 @@ void UsdMayaDiagnosticDelegate::InstallDelegate() // Note: waker accesses the flusher, so the waker must be created // after the flusher. _waker = std::make_unique(); + + _exitCallback = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, beforeExitCallback); } void UsdMayaDiagnosticDelegate::RemoveDelegate() @@ -423,6 +438,8 @@ void UsdMayaDiagnosticDelegate::RemoveDelegate() Flush(); + MMessage::removeCallback(_exitCallback); + // Note: waker accesses the flusher, so the waker must be destroyed // before the flusher. _waker.reset(); diff --git a/lib/mayaUsd/utils/targetLayer.cpp b/lib/mayaUsd/utils/targetLayer.cpp new file mode 100644 index 0000000000..89de165388 --- /dev/null +++ b/lib/mayaUsd/utils/targetLayer.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2023 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. +// + +#include "targetLayer.h" + +#include "dynamicAttribute.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace MAYAUSD_NS_DEF { + +MString convertTargetLayerToText(const PXR_NS::UsdStage& stage) +{ + MString text; + + const PXR_NS::UsdEditTarget target = stage.GetEditTarget(); + if (!target.IsValid()) + return text; + + const PXR_NS::SdfLayerHandle& layer = target.GetLayer(); + if (!layer) + return text; + + text = layer->GetIdentifier().c_str(); + + return text; +} + +void setTargetLayerFromText(PXR_NS::UsdStage& stage, const MString& text) +{ + if (text.length() == 0) + return; + + const std::string layerId(text.asChar()); + for (const auto& layer : stage.GetLayerStack()) + if (layer->GetIdentifier() == layerId) + stage.SetEditTarget(layer); +} + +namespace { + +const char targetLayerAttrName[] = "usdStageTargetLayer"; + +} // namespace + +MStatus copyTargetLayerToAttribute(const PXR_NS::UsdStage& stage, MayaUsdProxyShapeBase& proxyShape) +{ + MObject proxyObj = proxyShape.thisMObject(); + if (proxyObj.isNull()) + return MS::kFailure; + + MFnDependencyNode depNode(proxyObj); + if (!hasDynamicAttribute(depNode, targetLayerAttrName)) + createDynamicAttribute(depNode, targetLayerAttrName); + + MString targetLayerText = convertTargetLayerToText(stage); + + // Don't set the attribute if it already has the same value to avoid + // update loops. + MString previousTargetLayerText; + getDynamicAttribute(depNode, targetLayerAttrName, previousTargetLayerText); + if (previousTargetLayerText == targetLayerText) + return MS::kSuccess; + + MStatus status = setDynamicAttribute(depNode, targetLayerAttrName, targetLayerText); + + return status; +} + +MStatus +copyTargetLayerFromAttribute(const MayaUsdProxyShapeBase& proxyShape, PXR_NS::UsdStage& stage) +{ + MObject proxyObj = proxyShape.thisMObject(); + if (proxyObj.isNull()) + return MS::kFailure; + + MFnDependencyNode depNode(proxyObj); + if (!hasDynamicAttribute(depNode, targetLayerAttrName)) + return MS::kNotFound; + + MString targetLayerText; + MStatus status = getDynamicAttribute(depNode, targetLayerAttrName, targetLayerText); + if (status == MS::kSuccess) + setTargetLayerFromText(stage, targetLayerText); + + return status; +} + +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/utils/targetLayer.h b/lib/mayaUsd/utils/targetLayer.h new file mode 100644 index 0000000000..5d89d8a231 --- /dev/null +++ b/lib/mayaUsd/utils/targetLayer.h @@ -0,0 +1,63 @@ +// +// Copyright 2023 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. +// +#ifndef MAYAUSD_TARGETLAYER_H +#define MAYAUSD_TARGETLAYER_H + +#include +#include + +#include + +#include + +namespace MAYAUSD_NS_DEF { + +// The current target layer is stage-level data. As such, it is not saved +// within the layer (i.e. in the USD files that have been staged.) The reason +// behind this is that two stages could have different target layers. So, the +// target layer cannot be a layer-level data. +// +// Furthermore, stages in USD are not saved but are a pure run-time entity, +// part of the hosting application. It is thus the host responsibility to save +// stage-level state. So, we need to explicitly save the target layer. +// +// We thus save the target layer in the proxy shape as an attribute. + +/*! \brief convert the stage target layer to a text format. + */ +MAYAUSD_CORE_PUBLIC +MString convertTargetLayerToText(const PXR_NS::UsdStage& stage); + +/*! \brief set the stage target layer from a text format. + */ +MAYAUSD_CORE_PUBLIC +void setTargetLayerFromText(PXR_NS::UsdStage& stage, const MString& text); + +/*! \brief copy the stage target layer in the corresponding attribute of the proxy shape. + */ +MAYAUSD_CORE_PUBLIC +MStatus +copyTargetLayerToAttribute(const PXR_NS::UsdStage& stage, MayaUsdProxyShapeBase& proxyShape); + +/*! \brief set the stage target layer from data in the corresponding attribute of the proxy shape. + */ +MAYAUSD_CORE_PUBLIC +MStatus +copyTargetLayerFromAttribute(const MayaUsdProxyShapeBase& proxyShape, PXR_NS::UsdStage& stage); + +} // namespace MAYAUSD_NS_DEF + +#endif diff --git a/lib/mayaUsd/utils/utilFileSystem.cpp b/lib/mayaUsd/utils/utilFileSystem.cpp index ba04f417bd..0f5602d8a1 100644 --- a/lib/mayaUsd/utils/utilFileSystem.cpp +++ b/lib/mayaUsd/utils/utilFileSystem.cpp @@ -117,27 +117,41 @@ std::string UsdMayaUtilFileSystem::getMayaSceneFileDir() return std::string(); } -std::string UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(const std::string& fileName) +std::pair UsdMayaUtilFileSystem::makePathRelativeTo( + const std::string& fileName, + const std::string& relativeToDir) { ghc::filesystem::path absolutePath(fileName); - ghc::filesystem::path basePath(getMayaSceneFileDir()); - // If Maya scene file doesn't exist yet, use the absolute path - if (basePath.empty()) { - return fileName; + // If the anchor relative-to-directory doesn't exist yet, use the unchanged path, + // but don't return a failure. The anchor path being empty is not considered + // a failure. If the caller needs to detect this, they can verify that the + // anchor path is empty themselves before calling this function. + if (relativeToDir.empty()) { + return std::make_pair(fileName, true); } - ghc::filesystem::path relativePath = absolutePath.lexically_relative(basePath); + ghc::filesystem::path relativePath = absolutePath.lexically_relative(relativeToDir); if (relativePath.empty()) { + return std::make_pair(fileName, false); + } + + return std::make_pair(relativePath.generic_string(), true); +} + +std::string UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(const std::string& fileName) +{ + auto relativePathAndSuccess = makePathRelativeTo(fileName, getMayaSceneFileDir()); + + if (!relativePathAndSuccess.second) { TF_WARN( "File name (%s) cannot be resolved as relative to the Maya scene file, using the " "absolute path.", fileName.c_str()); - return fileName; } - return relativePath.generic_string(); + return relativePathAndSuccess.first; } bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() @@ -147,6 +161,14 @@ bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() && MGlobal::optionVarIntValue(MAKE_PATH_RELATIVE_TO_SCENE_FILE); } +bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToEditTargetLayer() +{ + static const MString MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE + = "mayaUsd_MakePathRelativeToEditTargetLayer"; + return MGlobal::optionVarExists(MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE) + && MGlobal::optionVarIntValue(MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE); +} + const char* getScenesFolderScript = R"( global proc string UsdMayaUtilFileSystem_GetScenesFolder() { diff --git a/lib/mayaUsd/utils/utilFileSystem.h b/lib/mayaUsd/utils/utilFileSystem.h index e0b5dea28f..34ed026cc5 100644 --- a/lib/mayaUsd/utils/utilFileSystem.h +++ b/lib/mayaUsd/utils/utilFileSystem.h @@ -34,6 +34,28 @@ std::string resolvePath(const std::string& filePath); MAYAUSD_CORE_PUBLIC std::string getDir(const std::string& fullFilePath); +/*! \brief Takes in two absolute file paths and computes a relative path of the first one + to second one. + + \return A pair with the path and a boolean that indicates if the attempt to make the + file name relative to the valid anchor path failed. + + If the anchor relative-to-directory is empty, then the original file name + is returned but no failure is returned. If the caller needs to detect + this as a failure case, they can verify that the relative-to directory + name is empty themselves before calling this function. + + The rationale for this is that, for example, we don't want to flag as + an error when the user tries to make a path relative to the scene when + the scene has not yet been saved. + + If the second path is not absolute or is not reachable from the first, + then the returned path will still be absolute. + */ +MAYAUSD_CORE_PUBLIC +std::pair +makePathRelativeTo(const std::string& fileName, const std::string& relativeToDir); + /*! \brief returns parent directory of a maya scene file opened by reference */ MAYAUSD_CORE_PUBLIC @@ -55,12 +77,18 @@ When there is no scene file, the absolute (input) path will be returned. MAYAUSD_CORE_PUBLIC std::string getPathRelativeToMayaSceneFile(const std::string& fileName); -/*! \brief returns the flag specifying whether Usd file paths should be saevd as relative to Maya +/*! \brief returns the flag specifying whether USD file paths should be saved as relative to Maya * scene file */ MAYAUSD_CORE_PUBLIC bool requireUsdPathsRelativeToMayaSceneFile(); +/*! \brief returns the flag specifying whether USD file paths should be saved + * as relative to the current edit target layer. + */ +MAYAUSD_CORE_PUBLIC +bool requireUsdPathsRelativeToEditTargetLayer(); + /*! \brief returns a unique file name */ MAYAUSD_CORE_PUBLIC diff --git a/lib/mayaUsd/utils/utilSerialization.cpp b/lib/mayaUsd/utils/utilSerialization.cpp index d41b1dbf1e..a17b4d754b 100644 --- a/lib/mayaUsd/utils/utilSerialization.cpp +++ b/lib/mayaUsd/utils/utilSerialization.cpp @@ -52,10 +52,11 @@ class RecursionDetector }; void populateChildren( - SdfLayerRefPtr layer, - RecursionDetector* recursionDetector, - std::vector>& anonLayersToSave, - std::vector& dirtyLayersToSave) + const UsdStageRefPtr& stage, + SdfLayerRefPtr layer, + RecursionDetector* recursionDetector, + MayaUsd::utils::LayerInfos& anonLayersToSave, + std::vector& dirtyLayersToSave) { auto subPaths = layer->GetSubLayerPaths(); @@ -69,13 +70,16 @@ void populateChildren( std::string actualPath = PXR_NS::SdfComputeAssetPathRelativeToLayer(layer, path); auto subLayer = PXR_NS::SdfLayer::FindOrOpen(actualPath); if (subLayer && !recursionDetector->contains(subLayer->GetRealPath())) { - populateChildren(subLayer, recursionDetector, anonLayersToSave, dirtyLayersToSave); + populateChildren( + stage, subLayer, recursionDetector, anonLayersToSave, dirtyLayersToSave); if (subLayer->IsAnonymous()) { - MayaUsd::utils::LayerParent p; - p._proxyPath.clear(); - p._layerParent = layer; - anonLayersToSave.push_back(std::make_pair(subLayer, p)); + MayaUsd::utils::LayerInfo info; + info.stage = stage; + info.layer = subLayer; + info.parent._proxyPath.clear(); + info.parent._layerParent = layer; + anonLayersToSave.push_back(info); } else if (subLayer->IsDirty()) { dirtyLayersToSave.push_back(subLayer); } @@ -269,16 +273,18 @@ bool saveLayerWithFormat( } SdfLayerRefPtr saveAnonymousLayer( + UsdStageRefPtr stage, SdfLayerRefPtr anonLayer, LayerParent parent, const std::string& basename, std::string formatArg) { std::string newFileName = generateUniqueFileName(basename); - return saveAnonymousLayer(anonLayer, newFileName, false, parent, formatArg); + return saveAnonymousLayer(stage, anonLayer, newFileName, false, parent, formatArg); } SdfLayerRefPtr saveAnonymousLayer( + UsdStageRefPtr stage, SdfLayerRefPtr anonLayer, const std::string& path, bool savePathAsRelative, @@ -292,6 +298,8 @@ SdfLayerRefPtr saveAnonymousLayer( std::string filePath(path); ensureUSDFileExtension(filePath); + const bool wasTargetLayer = (stage->GetEditTarget().GetLayer() == anonLayer); + saveLayerWithFormat(anonLayer, filePath, formatArg); SdfLayerRefPtr newLayer = SdfLayer::FindOrOpen(filePath); @@ -303,6 +311,10 @@ SdfLayerRefPtr saveAnonymousLayer( saveRootLayer(newLayer, parent._proxyPath, savePathAsRelative); } } + + if (wasTargetLayer) + stage->SetEditTarget(newLayer); + return newLayer; } @@ -318,7 +330,7 @@ void ensureUSDFileExtension(std::string& filePath) } } -void getLayersToSaveFromProxy(const std::string& proxyPath, stageLayersToSave& layersInfo) +void getLayersToSaveFromProxy(const std::string& proxyPath, StageLayersToSave& layersInfo) { auto stage = UsdMayaUtil::GetStageByProxyName(proxyPath); if (!stage) { @@ -326,18 +338,22 @@ void getLayersToSaveFromProxy(const std::string& proxyPath, stageLayersToSave& l } auto root = stage->GetRootLayer(); - populateChildren(root, nullptr, layersInfo._anonLayers, layersInfo._dirtyFileBackedLayers); + populateChildren( + stage, root, nullptr, layersInfo._anonLayers, layersInfo._dirtyFileBackedLayers); if (root->IsAnonymous()) { - LayerParent p; - p._proxyPath = proxyPath; - p._layerParent = nullptr; - layersInfo._anonLayers.push_back(std::make_pair(root, p)); + LayerInfo info; + info.stage = stage; + info.layer = root; + info.parent._proxyPath = proxyPath; + info.parent._layerParent = nullptr; + layersInfo._anonLayers.push_back(info); } else if (root->IsDirty()) { layersInfo._dirtyFileBackedLayers.push_back(root); } auto session = stage->GetSessionLayer(); - populateChildren(session, nullptr, layersInfo._anonLayers, layersInfo._dirtyFileBackedLayers); + populateChildren( + stage, session, nullptr, layersInfo._anonLayers, layersInfo._dirtyFileBackedLayers); } } // namespace utils diff --git a/lib/mayaUsd/utils/utilSerialization.h b/lib/mayaUsd/utils/utilSerialization.h index 02930a7032..2b6cebe7ae 100644 --- a/lib/mayaUsd/utils/utilSerialization.h +++ b/lib/mayaUsd/utils/utilSerialization.h @@ -76,10 +76,19 @@ struct LayerParent std::string _proxyPath; }; -struct stageLayersToSave +struct LayerInfo { - std::vector> _anonLayers; - std::vector _dirtyFileBackedLayers; + UsdStageRefPtr stage; + SdfLayerRefPtr layer; + MayaUsd::utils::LayerParent parent; +}; + +using LayerInfos = std::vector; + +struct StageLayersToSave +{ + LayerInfos _anonLayers; + std::vector _dirtyFileBackedLayers; }; /*! \brief Save an layer to disk to the given file path and using the given format. @@ -98,6 +107,7 @@ bool saveLayerWithFormat( */ MAYAUSD_CORE_PUBLIC PXR_NS::SdfLayerRefPtr saveAnonymousLayer( + PXR_NS::UsdStageRefPtr stage, PXR_NS::SdfLayerRefPtr anonLayer, LayerParent parent, const std::string& basename, @@ -108,6 +118,7 @@ PXR_NS::SdfLayerRefPtr saveAnonymousLayer( */ MAYAUSD_CORE_PUBLIC PXR_NS::SdfLayerRefPtr saveAnonymousLayer( + PXR_NS::UsdStageRefPtr stage, PXR_NS::SdfLayerRefPtr anonLayer, const std::string& path, bool savePathAsRelative, @@ -123,7 +134,7 @@ void ensureUSDFileExtension(std::string& filePath); layers that will need to be saved. */ MAYAUSD_CORE_PUBLIC -void getLayersToSaveFromProxy(const std::string& proxyPath, stageLayersToSave& layersInfo); +void getLayersToSaveFromProxy(const std::string& proxyPath, StageLayersToSave& layersInfo); } // namespace utils } // namespace MAYAUSD_NS_DEF diff --git a/lib/usd/ui/layerEditor/batchSaveLayersUIDelegate.cpp b/lib/usd/ui/layerEditor/batchSaveLayersUIDelegate.cpp index 5fa7d44742..2b6cd6dba1 100644 --- a/lib/usd/ui/layerEditor/batchSaveLayersUIDelegate.cpp +++ b/lib/usd/ui/layerEditor/batchSaveLayersUIDelegate.cpp @@ -48,10 +48,10 @@ UsdLayerEditor::batchSaveLayersUIDelegate(const std::vectorstageEntry(); - MayaUsd::utils::getLayersToSaveFromProxy(stageEntry._proxyShapePath, stageLayersToSave); - showConfirmDgl = !stageLayersToSave._anonLayers.empty(); + MayaUsd::utils::getLayersToSaveFromProxy(stageEntry._proxyShapePath, StageLayersToSave); + showConfirmDgl = !StageLayersToSave._anonLayers.empty(); } if (showConfirmDgl) { diff --git a/lib/usd/ui/layerEditor/saveLayersDialog.cpp b/lib/usd/ui/layerEditor/saveLayersDialog.cpp index 8e357df27c..943d91a1bb 100644 --- a/lib/usd/ui/layerEditor/saveLayersDialog.cpp +++ b/lib/usd/ui/layerEditor/saveLayersDialog.cpp @@ -98,9 +98,10 @@ class SaveLayersDialog; class SaveLayerPathRow : public QWidget { public: - SaveLayerPathRow( - SaveLayersDialog* in_parent, - const std::pair& in_layerPair); + using LayerInfo = MayaUsd::utils::LayerInfo; + using LayerInfos = MayaUsd::utils::LayerInfos; + + SaveLayerPathRow(SaveLayersDialog* in_parent, const LayerInfo& in_layerInfo); QString layerDisplayName() const; @@ -113,20 +114,18 @@ class SaveLayerPathRow : public QWidget void onTextChanged(const QString& text); public: - QString _pathToSaveAs; - SaveLayersDialog* _parent { nullptr }; - std::pair _layerPair; - QLabel* _label { nullptr }; - QLineEdit* _pathEdit { nullptr }; - QAbstractButton* _openBrowser { nullptr }; + QString _pathToSaveAs; + SaveLayersDialog* _parent { nullptr }; + LayerInfo _layerInfo; + QLabel* _label { nullptr }; + QLineEdit* _pathEdit { nullptr }; + QAbstractButton* _openBrowser { nullptr }; }; -SaveLayerPathRow::SaveLayerPathRow( - SaveLayersDialog* in_parent, - const std::pair& in_layerPair) +SaveLayerPathRow::SaveLayerPathRow(SaveLayersDialog* in_parent, const LayerInfo& in_layerInfo) : QWidget(in_parent) , _parent(in_parent) - , _layerPair(in_layerPair) + , _layerInfo(in_layerInfo) { auto gridLayout = new QGridLayout(); QtUtils::initLayoutMargins(gridLayout); @@ -134,14 +133,14 @@ SaveLayerPathRow::SaveLayerPathRow( // Since this is an anonymous layer, it should only be associated with a single stage. std::string stageName; const auto& stageLayers = in_parent->stageLayers(); - if (TF_VERIFY(1 == stageLayers.count(_layerPair.first))) { - auto search = stageLayers.find(_layerPair.first); + if (TF_VERIFY(1 == stageLayers.count(_layerInfo.layer))) { + auto search = stageLayers.find(_layerInfo.layer); stageName = search->second; } - QString displayName = _layerPair.first->GetDisplayName().c_str(); + QString displayName = _layerInfo.layer->GetDisplayName().c_str(); _label = new QLabel(displayName); - _label->setToolTip(in_parent->buildTooltipForLayer(_layerPair.first)); + _label->setToolTip(in_parent->buildTooltipForLayer(_layerInfo.layer)); gridLayout->addWidget(_label, 0, 0); _pathToSaveAs = MayaUsd::utils::generateUniqueFileName(stageName).c_str(); @@ -245,12 +244,14 @@ SaveLayersDialog::SaveLayersDialog( for (const auto& info : infos) { getLayersToSave( - info.dagPath.fullPathName().asChar(), info.dagPath.partialPathName().asChar()); + info.stage, + info.dagPath.fullPathName().asChar(), + info.dagPath.partialPathName().asChar()); } QString msg1, msg2; getDialogMessages( - static_cast(infos.size()), static_cast(_anonLayerPairs.size()), msg1, msg2); + static_cast(infos.size()), static_cast(_anonLayerInfos.size()), msg1, msg2); buildDialog(msg1, msg2); } #endif @@ -266,28 +267,31 @@ SaveLayersDialog::SaveLayersDialog(SessionState* in_sessionState, QWidget* in_pa std::string stageName = stageEntry._displayName; msg.format(StringResources::getAsMString(StringResources::kSaveName), stageName.c_str()); dialogTitle = MQtUtil::toQString(msg); - getLayersToSave(stageEntry._proxyShapePath, stageName); + getLayersToSave(stageEntry._stage, stageEntry._proxyShapePath, stageName); } setWindowTitle(dialogTitle); QString msg1, msg2; - getDialogMessages(1, static_cast(_anonLayerPairs.size()), msg1, msg2); + getDialogMessages(1, static_cast(_anonLayerInfos.size()), msg1, msg2); buildDialog(msg1, msg2); } SaveLayersDialog ::~SaveLayersDialog() { QApplication::restoreOverrideCursor(); } -void SaveLayersDialog::getLayersToSave(const std::string& proxyPath, const std::string& stageName) +void SaveLayersDialog::getLayersToSave( + const PXR_NS::UsdStageRefPtr& stage, + const std::string& proxyPath, + const std::string& stageName) { // Get the layers to save for this stage. - MayaUsd::utils::stageLayersToSave stageLayersToSave; - MayaUsd::utils::getLayersToSaveFromProxy(proxyPath, stageLayersToSave); + MayaUsd::utils::StageLayersToSave StageLayersToSave; + MayaUsd::utils::getLayersToSaveFromProxy(proxyPath, StageLayersToSave); // Keep track of all the layers for this particular stage. - for (const auto& layerPairs : stageLayersToSave._anonLayers) { - _stageLayerMap.emplace(std::make_pair(layerPairs.first, stageName)); + for (const auto& layerInfo : StageLayersToSave._anonLayers) { + _stageLayerMap.emplace(std::make_pair(layerInfo.layer, stageName)); } - for (const auto& dirtyLayer : stageLayersToSave._dirtyFileBackedLayers) { + for (const auto& dirtyLayer : StageLayersToSave._dirtyFileBackedLayers) { _stageLayerMap.emplace(std::make_pair(dirtyLayer, stageName)); } @@ -295,10 +299,10 @@ void SaveLayersDialog::getLayersToSave(const std::string& proxyPath, const std:: // Note: we use a set for the dirty file back layers because they // can come from multiple stages, but we only want them to // appear once in the dialog. - moveAppendVector(stageLayersToSave._anonLayers, _anonLayerPairs); + moveAppendVector(StageLayersToSave._anonLayers, _anonLayerInfos); _dirtyFileBackedLayers.insert( - std::begin(stageLayersToSave._dirtyFileBackedLayers), - std::end(stageLayersToSave._dirtyFileBackedLayers)); + std::begin(StageLayersToSave._dirtyFileBackedLayers), + std::end(StageLayersToSave._dirtyFileBackedLayers)); } void SaveLayersDialog::buildDialog(const QString& msg1, const QString& msg2) @@ -319,7 +323,7 @@ void SaveLayersDialog::buildDialog(const QString& msg1, const QString& msg2) buttonsLayout->addWidget(okButton); buttonsLayout->addWidget(cancelButton); - const bool haveAnonLayers { !_anonLayerPairs.empty() }; + const bool haveAnonLayers { !_anonLayerInfos.empty() }; const bool haveFileBackedLayers { !_dirtyFileBackedLayers.empty() }; SaveLayerPathRowArea* anonScrollArea { nullptr }; SaveLayerPathRowArea* fileScrollArea { nullptr }; @@ -331,7 +335,7 @@ void SaveLayersDialog::buildDialog(const QString& msg1, const QString& msg2) anonLayout->setContentsMargins(margin, margin, margin, 0); anonLayout->setSpacing(DPIScale(8)); anonLayout->setAlignment(Qt::AlignTop); - for (auto iter = _anonLayerPairs.cbegin(); iter != _anonLayerPairs.cend(); ++iter) { + for (auto iter = _anonLayerInfos.cbegin(); iter != _anonLayerInfos.cend(); ++iter) { auto row = new SaveLayerPathRow(this, (*iter)); anonLayout->addWidget(row); } @@ -467,13 +471,14 @@ void SaveLayersDialog::onSaveAll() QLayout* anonLayout = _anonLayersWidget->layout(); for (i = 0, count = anonLayout->count(); i < count; ++i) { auto row = dynamic_cast(anonLayout->itemAt(i)->widget()); - if (!row || !row->_layerPair.first) + if (!row || !row->_layerInfo.layer) continue; QString path = row->pathToSaveAs(); if (!path.isEmpty()) { - auto sdfLayer = row->_layerPair.first; - auto parent = row->_layerPair.second; + auto sdfLayer = row->_layerInfo.layer; + auto parent = row->_layerInfo.parent; + auto stage = row->_layerInfo.stage; auto qFileName = row->pathToSaveAs(); // If the qFileName is a relative path, compute the absolute path from the scene @@ -488,7 +493,7 @@ void SaveLayersDialog::onSaveAll() auto sFileName = qFileName.toStdString(); auto newLayer = MayaUsd::utils::saveAnonymousLayer( - sdfLayer, sFileName, savePathAsRelative, parent); + stage, sdfLayer, sFileName, savePathAsRelative, parent); if (newLayer) { _newPaths.append(QString::fromStdString(sdfLayer->GetDisplayName())); _newPaths.append(qFileName); diff --git a/lib/usd/ui/layerEditor/saveLayersDialog.h b/lib/usd/ui/layerEditor/saveLayersDialog.h index ec1af67d17..3dd8892e60 100644 --- a/lib/usd/ui/layerEditor/saveLayersDialog.h +++ b/lib/usd/ui/layerEditor/saveLayersDialog.h @@ -60,18 +60,21 @@ class SaveLayersDialog : public QDialog private: void buildDialog(const QString& msg1, const QString& msg2); - void getLayersToSave(const std::string& proxyPath, const std::string& stageName); + void getLayersToSave( + const UsdStageRefPtr& stage, + const std::string& proxyPath, + const std::string& stageName); private: - typedef std::vector> layerPairs; - typedef std::unordered_set layerSet; + typedef std::unordered_set layerSet; + using LayerInfos = MayaUsd::utils::LayerInfos; QStringList _newPaths; QStringList _problemLayers; QStringList _emptyLayers; QWidget* _anonLayersWidget { nullptr }; QWidget* _fileLayersWidget { nullptr }; - layerPairs _anonLayerPairs; + LayerInfos _anonLayerInfos; layerSet _dirtyFileBackedLayers; stageLayerMap _stageLayerMap; SessionState* _sessionState; diff --git a/plugin/adsk/scripts/mayaUSDRegisterStrings.py b/plugin/adsk/scripts/mayaUSDRegisterStrings.py index 502f79acd9..ab3c478751 100644 --- a/plugin/adsk/scripts/mayaUSDRegisterStrings.py +++ b/plugin/adsk/scripts/mayaUSDRegisterStrings.py @@ -36,4 +36,6 @@ def mayaUSDRegisterStrings(): register("kLoadUSDFile", "Load USD File") register("kFileOptions", "File Options") register("kMakePathRelativeToSceneFile", "Make Path Relative to Scene File") - register("kMakePathRelativeToSceneFileAnn", "If enabled, path will be relative to your Maya scene file. If this option is disabled, there is no Maya scene file and the path will be absolute. Save your Maya scene file to disk to make this option available.") + register("kMakePathRelativeToSceneFileAnn", "If enabled, path will be relative to your Maya scene file.\nIf this option is disabled, there is no Maya scene file and the path will be absolute.\nSave your Maya scene file to disk to make this option available.") + register("kMakePathRelativeToEditTargetLayer", "Make Path Relative to Edit Target Layer Directory") + register("kMakePathRelativeToEditTargetLayerAnn", "Enable to activate relative pathing to your current edit target layer’s directory.\nIf this option is disabled, verify that your target layer is not anonymous and save it to disk.") diff --git a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py index 1430b0d538..60ebf7462a 100644 --- a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py +++ b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py @@ -3,18 +3,51 @@ from mayaUSDRegisterStrings import getMayaUsdString from mayaUsdMayaReferenceUtils import pushOptionsUITemplate -class usdRootFileRelative(object): - fileNameEditField = None +class usdFileRelative(object): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative. - kMakePathRelativeCheckBox = 'MakePathRelative' + The caller must tell each function to what the file should be made relative to. + The 'what' is used to select the correct UI element, UI label, UI tool-tip and + to read and write the correct option var. + + For example by passing 'SceneFile', the functions will use: + UI element: MakePathRelativeToSceneFile + UI label: kMakePathRelativeToSceneFile + UI tool-tip: kMakePathRelativeToSceneFileAnn + option var: mayaUsd_MakePathRelativeToSceneFile + ''' + + kMakePathRelativeCheckBox = 'MakePathRelativeTo' + + _relativeToFilePath = None @classmethod - def uiCreate(cls, parentLayout): + def setRelativeFilePathRoot(cls, filePath): + ''' + Sets to which file the relative file will be anchored. + Set to empty or None to have the file be absolute, not relative. + ''' + cls._relativeToFilePath = filePath + + @classmethod + def getRelativeFilePathRoot(cls): + ''' + Gets to which file the relative file will be anchored. + Empty or None to have the file be absolute, not relative. + ''' + return cls._relativeToFilePath + + @classmethod + def uiCreate(cls, parentLayout, relativeToWhat): """ - Helper method to create the UI layout for the USD root file relative actions. + Helper method to create the UI layout for the file relative actions. Input parentLayout arg is expected to the a scroll layout into which controls can be added. + + Input relativeToWhat tells what the file is relative to. See the class docs. """ pushOptionsUITemplate() cmds.setParent(parentLayout) @@ -32,32 +65,113 @@ def uiCreate(cls, parentLayout): topForm = cmds.columnLayout('actionOptionsForm', rowSpacing=5) kFileOptionsStr = getMayaUsdString("kFileOptions") - kMakePathRelativeStr = getMayaUsdString("kMakePathRelativeToSceneFile") - kMakePathRelativeAnnStr = getMayaUsdString("kMakePathRelativeToSceneFileAnn") + kMakePathRelativeStr = getMayaUsdString("kMakePathRelativeTo" + relativeToWhat) + kMakePathRelativeAnnStr = getMayaUsdString("kMakePathRelativeTo" + relativeToWhat + "Ann") optBoxMarginWidth = mel.eval('global int $gOptionBoxTemplateDescriptionMarginWidth; $gOptionBoxTemplateDescriptionMarginWidth += 0') cmds.setParent(topForm) cmds.frameLayout(label=kFileOptionsStr, collapsable=False) widgetColumn = cmds.columnLayout() - cmds.checkBox(cls.kMakePathRelativeCheckBox, label=kMakePathRelativeStr, ann=kMakePathRelativeAnnStr) + cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, label=kMakePathRelativeStr, ann=kMakePathRelativeAnnStr) @classmethod - def uiInit(cls, parentLayout, filterType): + def uiInit(cls, parentLayout, canBeRelative, relativeToWhat): + """ + Helper method to initialize the UI layout for the file relative actions. + + Input parentLayout arg is expected to be a scroll layout into which controls + can be added. + + Input canBeRelative tells if the file can be made relative at all. If false, + the relative path UI is shown but disabled. + + Input relativeToWhat tells what the file is relative to. See the class docs. + """ cmds.setParent(parentLayout) # Get the current checkbox value from optionVar (if any) and update checkbox. - if cmds.optionVar(exists='mayaUsd_MakePathRelativeToSceneFile'): - relative = cmds.optionVar(query='mayaUsd_MakePathRelativeToSceneFile') - cmds.checkBox(cls.kMakePathRelativeCheckBox, edit=True, value=relative) + if cmds.optionVar(exists='mayaUsd_MakePathRelativeTo' + relativeToWhat): + relative = cmds.optionVar(query='mayaUsd_MakePathRelativeTo' + relativeToWhat) + cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, edit=True, value=relative) + + # If if cannot be relative, then the checkbox and label should be disabled. + cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, edit=True, enable=canBeRelative) + + @classmethod + def uiCommit(cls, parentLayout, relativeToWhat): + """ + Helper method to commit the UI layout for the file relative actions. + + Input parentLayout arg is expected to the a scroll layout into which controls + can be added. + Input relativeToWhat tells what the file is relative to. See the class docs. + """ + cmds.setParent(parentLayout) + + # Get the current checkbox state and save to optionVar. + relative = cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, query=True, value=True) + cmds.optionVar(iv=('mayaUsd_MakePathRelativeTo' + relativeToWhat, relative)) + + +class usdRootFileRelative(usdFileRelative): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative to the Maya scene file. + ''' + + kRelativeToWhat = 'SceneFile' + + @classmethod + def uiCreate(cls, parentLayout): + usdFileRelative.uiCreate(parentLayout, cls.kRelativeToWhat) + + @classmethod + def uiInit(cls, parentLayout, filterType): + ''' + Note: the function takes an unused filterType argument to be compatible + with the dialog2 command API. + ''' # If there is no Maya scene file saved, then the checkbox and label should be disabled. haveSceneFile = cmds.file(q=True, exists=True) - cmds.checkBox(cls.kMakePathRelativeCheckBox, edit=True, enable=haveSceneFile) + usdFileRelative.setRelativeFilePathRoot(cmds.file(query=True, sceneName=True)) + usdFileRelative.uiInit(parentLayout, haveSceneFile, cls.kRelativeToWhat) @classmethod def uiCommit(cls, parentLayout, selectedFile=None): - cmds.setParent(parentLayout) + ''' + Note: the function takes an unused selectedFile argument to be compatible + with the dialog2 command API. + ''' + usdFileRelative.uiCommit(parentLayout, cls.kRelativeToWhat) - # Get the current checkbox state and save to optionVar. - relative = cmds.checkBox(cls.kMakePathRelativeCheckBox, query=True, value=True) - cmds.optionVar(iv=('mayaUsd_MakePathRelativeToSceneFile', relative)) + +class usdFileRelativeToEditTargetLayer(usdFileRelative): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative to a layer file. + ''' + + kRelativeToWhat = 'EditTargetLayer' + + @classmethod + def uiCreate(cls, parentLayout): + usdFileRelative.uiCreate(parentLayout, cls.kRelativeToWhat) + + @classmethod + def uiInit(cls, parentLayout, filterType): + ''' + Note: the function takes an unused filterType argument to be compatible + with the dialog2 command API. + ''' + # If there is no target layer saved, then the checkbox and label should be disabled. + canBeRelative = bool(usdFileRelative.getRelativeFilePathRoot()) + usdFileRelative.uiInit(parentLayout, canBeRelative, cls.kRelativeToWhat) + + @classmethod + def uiCommit(cls, parentLayout, selectedFile=None): + ''' + Note: the function takes an unused selectedFile argument to be compatible + with the dialog2 command API. + ''' + usdFileRelative.uiCommit(parentLayout, cls.kRelativeToWhat) diff --git a/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel b/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel index b1fbc5060d..6627ea53d7 100644 --- a/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel +++ b/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel @@ -48,6 +48,7 @@ global proc mayaUsd_pluginUICreation() // is no mechanism to source such a file once, if multiple plugins are // loaded, so source it here in this plugin. PPT, 29-Nov-2021. source "mayaUsdAddMayaReference.mel"; + source "mayaUsdAddUSDReference.mel"; source "mayaUsdCacheMayaReference.mel"; source "mayaUsdMergeToUSDOptions.mel"; source "mayaUsdDuplicateAsMayaDataOptions.mel"; diff --git a/plugin/pxr/maya/lib/usdMaya/testenv/testUsdExportPackage.py b/plugin/pxr/maya/lib/usdMaya/testenv/testUsdExportPackage.py index 52f5611890..fb8baa13c1 100644 --- a/plugin/pxr/maya/lib/usdMaya/testenv/testUsdExportPackage.py +++ b/plugin/pxr/maya/lib/usdMaya/testenv/testUsdExportPackage.py @@ -42,6 +42,7 @@ def tearDownClass(cls): def setUp(self): cmds.file(os.path.abspath('PackageTest.ma'), open=True, force=True) + self._existingTempFiles = set() def _AssertExpectedStage(self, stage, cardPngRefPath): self.assertTrue(stage) @@ -63,14 +64,24 @@ def _AssertExpectedStage(self, stage, cardPngRefPath): self.assertEqual(modelAPI.GetModelCardTextureXNegAttr().Get().path, cardPngRefPath) + def _listOfTempFiles(self, usdFilePath): + return os.listdir(os.path.dirname(usdFilePath)) + + def _recordExistingTempFiles(self, usdFilePath): + self._existingTempFiles = set(self._listOfTempFiles(usdFilePath)) + def _AssertNoTempFiles(self, usdFilePath): - lsDir = os.listdir(os.path.dirname(usdFilePath)) + lsDir = self._listOfTempFiles(usdFilePath) + lsDir = [file for file in lsDir if file not in self._existingTempFiles] for item in lsDir: self.assertNotRegex(item, "tmp-.*\.usd.?") def testExport(self): '''Tests standard usdz package export.''' usdFile = os.path.abspath('MyAwesomePackage.usdz') + + self._recordExistingTempFiles(usdFile) + cmds.usdExport( file=usdFile, mergeTransformAndShape=True, @@ -102,6 +113,8 @@ def testArKitCompatibility(self): usdFile = os.path.abspath('MyAwesomeArKitCompatibleFile.usdz') usdFileNoExt = os.path.abspath('MyAwesomeArKitCompatibleFile') + self._recordExistingTempFiles(usdFile) + # The usdExport command should automatically add "usdz" extension since # we're requestion appleArKit compatibility. cmds.usdExport( diff --git a/test/lib/mayaUsd/nodes/testProxyShapeBase.py b/test/lib/mayaUsd/nodes/testProxyShapeBase.py index 8a593bf1b5..8fca99f280 100644 --- a/test/lib/mayaUsd/nodes/testProxyShapeBase.py +++ b/test/lib/mayaUsd/nodes/testProxyShapeBase.py @@ -44,10 +44,13 @@ def setUpClass(cls): def tearDownClass(cls): standalone.uninitialize() + def getTempFileName(self, filename): + return os.path.join( + self._currentTestDir, filename) + def setupEmptyScene(self): self._currentTestDir = tempfile.mkdtemp(prefix='ProxyShapeBaseTest') - tempMayaFile = os.path.join( - self._currentTestDir, 'ProxyShapeBaseScene.ma') + tempMayaFile = self.getTempFileName('ProxyShapeBaseScene.ma') cmds.file(new=True, force=True) cmds.file(rename=tempMayaFile) return tempMayaFile @@ -633,7 +636,7 @@ def testStageMutedLayers(self): # Verify there are no muted layers by default. self.assertListEqual([], stage.GetMutedLayers()) - # select the room and get its context menu. + # Change the muted layers. stage.MuteLayer("abc") stage.MuteLayer("def") @@ -652,6 +655,42 @@ def verifyMuting(stage): stage = mayaUsd.lib.GetPrim('|stage1|stageShape1').GetStage() verifyMuting(stage) + @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testStageTargetLayer only available in UFE v2 or greater.') + def testStageTargetLayer(self): + ''' + Verify that stage preserve the target layer of the stage when a scene is reloaded. + ''' + # create new scene + tempMayaFile = self.setupEmptyScene() + + # create an empty scene + shapePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(shapePath).GetStage() + + # Add a sub-layer and target it. + subLayerFileName = self.getTempFileName('targetSubLayer.usd') + usdFormat = Sdf.FileFormat.FindByExtension('usd') + subLayer = Sdf.Layer.New(usdFormat, subLayerFileName) + subLayer.Save() + stage.GetRootLayer().subLayerPaths.append(subLayer.identifier) + stage.SetEditTarget(subLayer) + + def verifyTargetLayer(stage): + self.assertNotEqual(stage.GetRootLayer().identifier, stage.GetEditTarget().GetLayer().identifier) + + verifyTargetLayer(stage) + + # Save the Maya scene. + cmds.file(save=True, force=True) + + # Reload the scene and verify the target layer is not the root layer. + cmds.file(new=True, force=True) + cmds.file(tempMayaFile, open=True) + + stage = mayaUsd.lib.GetPrim('|stage1|stageShape1').GetStage() + self.assertListEqual(list(stage.GetRootLayer().subLayerPaths), [subLayer.identifier]) + verifyTargetLayer(stage) + def testSerializationShareStage(self): ''' Verify share/unshare stage works with serialization and complex heirharchies diff --git a/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_flat_render.png b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_flat_render.png index a2d7fb579b..a4556dcf50 100644 Binary files a/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_flat_render.png and b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_flat_render.png differ diff --git a/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_render.png b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_render.png index bb9d7d21ba..03cdb31a2f 100644 Binary files a/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_render.png and b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_render.png differ diff --git a/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_untextured_render.png b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_untextured_render.png new file mode 100644 index 0000000000..363cfd0cd3 Binary files /dev/null and b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateMaterialXTest/baseline/MayaSurfaces_untextured_render.png differ diff --git a/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateMaterialX.py b/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateMaterialX.py index c2ef33c9ff..e9e3fc166c 100644 --- a/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateMaterialX.py +++ b/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateMaterialX.py @@ -57,10 +57,10 @@ def assertSnapshotClose(self, imageName, w=960, h=540): imageUtils.snapshot(snapshotImage, width=w, height=h) return self.assertImagesClose(baselineImage, snapshotImage) - def _StartTest(self, testName): + def _StartTest(self, testName, textured=True): mayaUtils.loadPlugin("mayaUsdPlugin") panel = mayaUtils.activeModelPanel() - cmds.modelEditor(panel, edit=True, displayTextures=True) + cmds.modelEditor(panel, edit=True, displayTextures=textured) self._testName = testName testFile = testUtils.getTestScene("MaterialX", self._testName + ".usda") @@ -89,6 +89,8 @@ def testMayaSurfaces(self): self._StartTest('MayaSurfaces_flat') cmds.modelEditor(panel, edit=True, displayLights="default") + self._StartTest('MayaSurfaces_untextured', False) + @unittest.skipIf(os.getenv('MATERIALX_VERSION', '1.38.0') < '1.38.4', 'Test has a glTf PBR surface only found in MaterialX 1.38.4 and later.') def testTransparency(self): mayaUtils.loadPlugin("mayaUsdPlugin") diff --git a/test/lib/mayaUsd/utils/CMakeLists.txt b/test/lib/mayaUsd/utils/CMakeLists.txt index 6e591c0aef..5b336f67e6 100644 --- a/test/lib/mayaUsd/utils/CMakeLists.txt +++ b/test/lib/mayaUsd/utils/CMakeLists.txt @@ -102,6 +102,10 @@ if(IS_WINDOWS) testLoadRules testLoadRules.cpp ) + add_mayaUsdLibUtils_test( + testTargetLayer + testTargetLayer.cpp + ) add_mayaUsdLibUtils_test( testSplitString testSplitString.cpp diff --git a/test/lib/mayaUsd/utils/testTargetLayer.cpp b/test/lib/mayaUsd/utils/testTargetLayer.cpp new file mode 100644 index 0000000000..624d569a6c --- /dev/null +++ b/test/lib/mayaUsd/utils/testTargetLayer.cpp @@ -0,0 +1,36 @@ +#include + +#include + +#include + +using namespace PXR_NS; +using namespace MAYAUSD_NS_DEF; + +TEST(ConvertTargetLayer, convertDefaultTargetLayer) +{ + auto originalStage = UsdStage::CreateInMemory(); + auto originalTargetLayer = originalStage->GetEditTarget(); + + const MString text = convertTargetLayerToText(*originalStage); + setTargetLayerFromText(*originalStage, text); + + auto restoredTargetLayer = originalStage->GetEditTarget(); + EXPECT_EQ(originalTargetLayer, restoredTargetLayer); +} + +TEST(ConvertTargetLayer, convertSubLayerTargetLayer) +{ + auto originalStage = UsdStage::CreateInMemory(); + auto subLayer = SdfLayer::CreateAnonymous(); + originalStage->GetRootLayer()->InsertSubLayerPath(subLayer->GetIdentifier()); + originalStage->SetEditTarget(subLayer); + + auto originalTargetLayer = originalStage->GetEditTarget(); + + const MString text = convertTargetLayerToText(*originalStage); + setTargetLayerFromText(*originalStage, text); + + auto restoredTargetLayer = originalStage->GetEditTarget(); + EXPECT_EQ(originalTargetLayer, restoredTargetLayer); +} diff --git a/test/lib/testMayaUsdCreateStageCommands.py b/test/lib/testMayaUsdCreateStageCommands.py index 901a207aa5..ae6df68303 100644 --- a/test/lib/testMayaUsdCreateStageCommands.py +++ b/test/lib/testMayaUsdCreateStageCommands.py @@ -53,6 +53,11 @@ def testCreateStageWithNewLayer(self): self.assertTrue(cmds.isConnected('time1.outTime', shapeNode+'.time')) def testCreateStageFromFile(self): + # Open top_layer.ma and make sure to have USD stage paths as absolute + mayaSceneFilePath = testUtils.getTestScene("ballset", "StandaloneScene", "top_layer.ma") + cmds.file(mayaSceneFilePath, force=True, open=True) + cmds.optionVar(iv=('mayaUsd_MakePathRelativeToSceneFile', 0)) + # We cannot directly call the 'mayaUsdCreateStageFromFile' # as it opens a file dialog to choose the scene. So instead # we can call what it does once the file is choose. @@ -72,3 +77,14 @@ def testCreateStageFromFile(self): # Verify that the shape node is connected to time. self.assertTrue(cmds.isConnected('time1.outTime', shapeNode+'.time')) + + # Now switch to having USD stage paths as relative to Maya scene file + cmds.optionVar(iv=('mayaUsd_MakePathRelativeToSceneFile', 1)) + + # Create the same stage and verify that now it's open as relative + shapeNodeRel = mel.eval('mayaUsd_createStageFromFilePath(\"'+ballFilePath+'\")') + filePathAttrRel = cmds.getAttr(shapeNodeRel+'.filePath') + self.assertEqual(filePathAttrRel, 'top_layer.usda') + + # Restore mayaUsd_MakePathRelativeToSceneFile + cmds.optionVar(iv=('mayaUsd_MakePathRelativeToSceneFile', 0)) \ No newline at end of file diff --git a/test/lib/testMayaUsdLayerEditorCommands.py b/test/lib/testMayaUsdLayerEditorCommands.py index b1317d1847..8b6c8b67f7 100644 --- a/test/lib/testMayaUsdLayerEditorCommands.py +++ b/test/lib/testMayaUsdLayerEditorCommands.py @@ -18,6 +18,7 @@ import unittest import tempfile +import testUtils from os import path from maya import cmds, mel import mayaUsd_createStageWithNewLayer @@ -557,3 +558,22 @@ def addFileBakedLayerByPath(parentLayer): testMuteLayerImpl(addAnonymousLayer) testMuteLayerImpl(addFileBakedLayer) testMuteLayerImpl(addFileBakedLayerByPath) + + def testPathRelativeToMayaSceneFile(self): + # Function mayaUsdLib.Util.getPathRelativeToMayaSceneFile is used during + # USD root layer saving/loading, and since we cannot test the full workflow + # as it opens a file dialog and requires user input, we make sure to test + # the function itself here. + + ballFilePath = path.normpath(testUtils.getTestScene('ballset', 'StandaloneScene', 'top_layer.usda')).replace('\\', '/') + + # Without Maya scene file, the absolute path is returned + cmds.file(newFile=True, force=True) + filePathAbs = mayaUsd.lib.Util.getPathRelativeToMayaSceneFile(ballFilePath) + self.assertEqual(filePathAbs, ballFilePath) + + # With Maya scene file, the relative path is returned + mayaSceneFilePath = testUtils.getTestScene("ballset", "StandaloneScene", "top_layer.ma") + cmds.file(mayaSceneFilePath, force=True, open=True) + filePathRel = mayaUsd.lib.Util.getPathRelativeToMayaSceneFile(ballFilePath) + self.assertEqual(filePathRel, 'top_layer.usda') diff --git a/test/lib/ufe/testConnections.py b/test/lib/ufe/testConnections.py index 60d91349aa..84176a5aa5 100644 --- a/test/lib/ufe/testConnections.py +++ b/test/lib/ufe/testConnections.py @@ -979,5 +979,179 @@ def testCompoundDisplacementPassthrough(self): cmd = connectionHandler.createConnectionCmd(compoundDisplacementAttr, materialDisplacementAttr) cmd.execute() + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '4043', 'Test only available in UFE preview version 0.4.43 and greater since it uses the UsdUndoDeleteConnectionCommand') + def testRemovePropertiesWithConnections(self): + '''Test delete connections and properties.''' + + # Load a scene. + + testFile = testUtils.getTestScene('properties', 'properties.usda') + shapeNode,shapeStage = mayaUtils.createProxyFromFile(testFile) + + ufeParentItem = ufeUtils.createUfeSceneItem(shapeNode, + '/mtl/UsdPreviewSurface1') + ufePreviewItem = ufeUtils.createUfeSceneItem(shapeNode, + '/mtl/UsdPreviewSurface1/UsdPreviewSurface1') + ufeSurfaceItem = ufeUtils.createUfeSceneItem(shapeNode, + '/mtl/UsdPreviewSurface1/surface1') + ufeCompoundItem = ufeUtils.createUfeSceneItem(shapeNode, + '/mtl/UsdPreviewSurface1/compound') + ufeChildCompoundItem = ufeUtils.createUfeSceneItem(shapeNode, + '/mtl/UsdPreviewSurface1/compound/UsdPreviewSurface2') + + self.assertIsNotNone(ufeParentItem) + self.assertIsNotNone(ufePreviewItem) + self.assertIsNotNone(ufeSurfaceItem) + self.assertIsNotNone(ufeCompoundItem) + self.assertIsNotNone(ufeChildCompoundItem) + + connectionHandler = ufe.RunTimeMgr.instance().connectionHandler(ufeParentItem.runTimeId()) + self.assertIsNotNone(connectionHandler) + + # Get the prims. + parentPrim = usdUtils.getPrimFromSceneItem(ufeParentItem) + previewPrim = usdUtils.getPrimFromSceneItem(ufePreviewItem) + surfacePrim = usdUtils.getPrimFromSceneItem(ufeSurfaceItem) + compoundPrim = usdUtils.getPrimFromSceneItem(ufeCompoundItem) + childCompoundPrim = usdUtils.getPrimFromSceneItem(ufeChildCompoundItem) + + # Get the attributes. + parentAttrs = ufe.Attributes.attributes(ufeParentItem) + previewAttrs = ufe.Attributes.attributes(ufePreviewItem) + surfaceAttrs = ufe.Attributes.attributes(ufeSurfaceItem) + compoundAttrs = ufe.Attributes.attributes(ufeCompoundItem) + childCompoundAttrs = ufe.Attributes.attributes(ufeChildCompoundItem) + + # 1. Delete node (not compound) connections. + + previewClearcoat = previewAttrs.attribute('inputs:clearcoat') + previewSurface = previewAttrs.attribute('outputs:surface') + parentClearcoat = parentAttrs.attribute('inputs:clearcoat') + parentSurface = parentAttrs.attribute('outputs:surface') + surfaceBsdf = surfaceAttrs.attribute('inputs:bsdf') + + self.assertIsNotNone(previewClearcoat) + self.assertIsNotNone(previewSurface) + self.assertIsNotNone(parentClearcoat) + self.assertIsNotNone(parentSurface) + self.assertIsNotNone(surfaceBsdf) + + # 1.1 Delete the connection between two nodes (not compounds). + cmd = connectionHandler.deleteConnectionCmd(previewSurface, surfaceBsdf) + cmd.execute() + + # Property kept since there is a connection to the parent. + self.assertTrue(previewPrim.HasProperty('outputs:surface')) + # The property has been deleted since there are no more connections. + self.assertFalse(surfacePrim.HasProperty('outputs:surface')) + + # 1.2 Delete the connection between the node and its parent (outputs). + cmd = connectionHandler.deleteConnectionCmd(previewSurface, parentSurface) + cmd.execute() + + # The property has been deleted since there are no more connections. + self.assertFalse(previewPrim.HasProperty('outputs:surface')) + # Property kept since it is on the boundary. + self.assertTrue(parentPrim.HasProperty('outputs:surface')) + + # 1.3 Delete the connection between the node and its parent (inputs). + cmd = connectionHandler.deleteConnectionCmd(parentClearcoat, previewClearcoat) + cmd.execute() + + # The property has been deleted since there are no more connections. + self.assertFalse(previewPrim.HasProperty('inputs:clearcoat')) + # Property kept since it is on the boundary. + self.assertTrue(parentPrim.HasProperty('inputs:clearcoat')) + + # 2. Delete compound connections. + + compoundDisplacement = compoundAttrs.attribute('outputs:displacement') + compoundPort = compoundAttrs.attribute('outputs:port') + parentDisplacement = parentAttrs.attribute('outputs:displacement') + parentPort = parentAttrs.attribute('outputs:port') + + self.assertIsNotNone(compoundDisplacement) + self.assertIsNotNone(compoundPort) + self.assertIsNotNone(parentDisplacement) + self.assertIsNotNone(parentPort) + + # 2.1 Delete compound connections to the parent. + cmd = connectionHandler.deleteConnectionCmd(compoundPort, parentPort) + cmd.execute() + + # Properties kept since they are on the boundary. + self.assertTrue(compoundPrim.HasProperty('outputs:port')) + self.assertTrue(parentPrim.HasProperty('outputs:port')) + + cmd = connectionHandler.deleteConnectionCmd(compoundDisplacement, parentDisplacement) + cmd.execute() + + # Properties kept since they are on the boundary. + self.assertTrue(compoundPrim.HasProperty('outputs:displacement')) + self.assertTrue(parentPrim.HasProperty('outputs:displacement')) + + # 2.2 Delete compound connections from the parent. + compoundClearcoatRoughness = compoundAttrs.attribute('inputs:clearcoatRoughness') + compoundPort = compoundAttrs.attribute('inputs:port') + parentClearcoatRoughness = parentAttrs.attribute('inputs:clearcoatRoughness') + parentPort = parentAttrs.attribute('inputs:port') + + self.assertIsNotNone(compoundClearcoatRoughness) + self.assertIsNotNone(compoundPort) + self.assertIsNotNone(parentClearcoatRoughness) + self.assertIsNotNone(parentPort) + + cmd = connectionHandler.deleteConnectionCmd(parentPort, compoundPort) + cmd.execute() + + # Properties kept since they are on the boundary. + self.assertTrue(compoundPrim.HasProperty('inputs:port')) + self.assertTrue(parentPrim.HasProperty('inputs:port')) + + cmd = connectionHandler.deleteConnectionCmd(parentClearcoatRoughness, compoundClearcoatRoughness) + cmd.execute() + + # Properties kept since they are on the boundary. + self.assertTrue(compoundPrim.HasProperty('inputs:clearcoatRoughness')) + self.assertTrue(parentPrim.HasProperty('inputs:clearcoatRoughness')) + + # 3. Delete connections inside the compound. + + childDisplacement = childCompoundAttrs.attribute('outputs:displacement') + childClearcoatRoughness = childCompoundAttrs.attribute('inputs:clearcoatRoughness') + childClearcoat = childCompoundAttrs.attribute('inputs:clearcoat') + compoundClearcoat = compoundAttrs.attribute('inputs:clearcoat') + + self.assertIsNotNone(childDisplacement) + self.assertIsNotNone(childClearcoatRoughness) + self.assertIsNotNone(childClearcoat) + self.assertIsNotNone(compoundClearcoat) + + # 3.1 Delete child compound connections to the parent. + cmd = connectionHandler.deleteConnectionCmd(childDisplacement, compoundDisplacement) + cmd.execute() + + # Property kept since it is on the boundary. + self.assertTrue(compoundPrim.HasProperty('outputs:displacement')) + # The property has been deleted since there are no more connections. + self.assertFalse(childCompoundPrim.HasProperty('outputs:displacement')) + + # 3.2 Delete child compound connections from the parent. + cmd = connectionHandler.deleteConnectionCmd(compoundClearcoatRoughness, childClearcoatRoughness) + cmd.execute() + + # Property kept since it is on the boundary. + self.assertTrue(compoundPrim.HasProperty('inputs:clearcoatRoughness')) + # The property has been deleted since there are no more connections. + self.assertFalse(childCompoundPrim.HasProperty('inputs:clearcoatRoughness')) + + cmd = connectionHandler.deleteConnectionCmd(compoundClearcoat, childClearcoat) + cmd.execute() + + # Property kept since it is on the boundary. + self.assertTrue(compoundPrim.HasProperty('inputs:clearcoat')) + # The property has been deleted since there are no more connections. + self.assertFalse(childCompoundPrim.HasProperty('inputs:clearcoat')) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/test/lib/ufe/testDisplayLayer.py b/test/lib/ufe/testDisplayLayer.py index e06b9963c1..b5b5c80db8 100644 --- a/test/lib/ufe/testDisplayLayer.py +++ b/test/lib/ufe/testDisplayLayer.py @@ -389,6 +389,16 @@ def testDisplayLayerDuplicate(self): self._testLayerFromPath(CUBE2, self.LAYER1) self._testLayerFromPath(XFORM2_CUBE1, self.LAYER1) + # Let's also test duplicating a node that is not the root child: XFORM1_CUBE1. + cmds.select(self.XFORM1_CUBE1) + cmds.duplicate() + + # Verify that the duplicate object is in the same display layer. + layerObjs = cmds.editDisplayLayerMembers(self.LAYER1, query=True, **self.kwArgsEditDisplayLayerMembers) + XFORM1_CUBE2 = '|stage1|stageShape1,/Xform1/Cube2' + self.assertTrue(XFORM1_CUBE2 in layerObjs) + self._testLayerFromPath(XFORM1_CUBE2, self.LAYER1) + def testDisplayLayerClear(self): cmdHelp = cmds.help('editDisplayLayerMembers') if '-clear' not in cmdHelp: diff --git a/test/lib/ufe/testVisibilityCmd.py b/test/lib/ufe/testVisibilityCmd.py index 33f4683d9e..b0657c5576 100644 --- a/test/lib/ufe/testVisibilityCmd.py +++ b/test/lib/ufe/testVisibilityCmd.py @@ -78,7 +78,7 @@ def tearDown(self): # Restore default edit router. mayaUsd.lib.restoreDefaultEditRouter('visibility') - def _testEditRouter(self): + def testEditRouter(self): '''Test edit router functionality.''' # Select /A diff --git a/test/testSamples/MaterialX/MayaSurfaces.usda b/test/testSamples/MaterialX/MayaSurfaces.usda index 9ef718a02a..e4871310c5 100644 --- a/test/testSamples/MaterialX/MayaSurfaces.usda +++ b/test/testSamples/MaterialX/MayaSurfaces.usda @@ -344,3 +344,128 @@ def Mesh "pPlatonic2" ( } } +def Mesh "pSphere1" ( + prepend apiSchemas = ["MaterialBindingAPI"] + kind = "component" +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + int[] faceVertexIndices = [1, 6, 8, 6, 7, 12, 6, 12, 8, 8, 12, 9, 7, 2, 10, 7, 10, 12, 12, 10, 11, 12, 11, 9, 9, 11, 0, 2, 13, 10, 13, 14, 17, 13, 17, 10, 10, 17, 11, 14, 3, 15, 14, 15, 17, 17, 15, 16, 17, 16, 11, 11, 16, 0, 3, 18, 15, 18, 19, 22, 18, 22, 15, 15, 22, 16, 19, 4, 20, 19, 20, 22, 22, 20, 21, 22, 21, 16, 16, 21, 0, 0, 21, 9, 21, 20, 25, 21, 25, 9, 9, 25, 8, 20, 4, 23, 20, 23, 25, 25, 23, 24, 25, 24, 8, 8, 24, 1, 5, 26, 28, 26, 27, 30, 26, 30, 28, 28, 30, 29, 27, 2, 7, 27, 7, 30, 30, 7, 6, 30, 6, 29, 29, 6, 1, 5, 31, 26, 31, 32, 33, 31, 33, 26, 26, 33, 27, 32, 3, 14, 32, 14, 33, 33, 14, 13, 33, 13, 27, 27, 13, 2, 5, 34, 31, 34, 35, 36, 34, 36, 31, 31, 36, 32, 35, 4, 19, 35, 19, 36, 36, 19, 18, 36, 18, 32, 32, 18, 3, 5, 28, 34, 28, 29, 37, 28, 37, 34, 34, 37, 35, 29, 1, 24, 29, 24, 37, 37, 24, 23, 37, 23, 35, 35, 23, 4] + rel material:binding = + normal3f[] normals = [(1, 0, 0), (0.8572056, 0.5149743, -3.262045e-8), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, 0.5149743, -3.262045e-8), (0.51497436, 0.85720557, -8.155112e-9), (0.5773503, 0.5773503, 0.57735026), (0.8572056, 0.5149743, -3.262045e-8), (0.5773503, 0.5773503, 0.57735026), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, -8.155113e-9, 0.5149743), (0.5773503, 0.5773503, 0.57735026), (0.5149743, 0, 0.8572056), (0.51497436, 0.85720557, -8.155112e-9), (0, 1, 0), (2.8542896e-8, 0.8572056, 0.51497436), (0.51497436, 0.85720557, -8.155112e-9), (2.8542896e-8, 0.8572056, 0.51497436), (0.5773503, 0.5773503, 0.57735026), (0.5773503, 0.5773503, 0.57735026), (2.8542896e-8, 0.8572056, 0.51497436), (-8.155111e-9, 0.51497436, 0.8572055), (0.5773503, 0.5773503, 0.57735026), (-8.155111e-9, 0.51497436, 0.8572055), (0.5149743, 0, 0.8572056), (0.5149743, 0, 0.8572056), (-8.155111e-9, 0.51497436, 0.8572055), (0, 0, 1), (0, 1, 0), (-0.51497436, 0.8572056, 8.155113e-9), (2.8542896e-8, 0.8572056, 0.51497436), (-0.51497436, 0.8572056, 8.155113e-9), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.57735026, 0.57735026, 0.5773503), (-0.51497436, 0.8572056, 8.155113e-9), (-0.57735026, 0.57735026, 0.5773503), (2.8542896e-8, 0.8572056, 0.51497436), (2.8542896e-8, 0.8572056, 0.51497436), (-0.57735026, 0.57735026, 0.5773503), (-8.155111e-9, 0.51497436, 0.8572055), (-0.85720557, 0.51497436, -1.6310224e-8), (-1, 0, 0), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.57735026, 0.57735026, 0.5773503), (-0.57735026, 0.57735026, 0.5773503), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.51497436, 2.8542892e-8, 0.8572056), (-0.57735026, 0.57735026, 0.5773503), (-0.51497436, 2.8542892e-8, 0.8572056), (-8.155111e-9, 0.51497436, 0.8572055), (-8.155111e-9, 0.51497436, 0.8572055), (-0.51497436, 2.8542892e-8, 0.8572056), (0, 0, 1), (-1, 0, 0), (-0.8572056, -0.5149743, -3.262045e-8), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.8572056, -0.5149743, -3.262045e-8), (-0.51497436, -0.85720557, -8.155112e-9), (-0.5773503, -0.5773503, 0.57735026), (-0.8572056, -0.5149743, -3.262045e-8), (-0.5773503, -0.5773503, 0.57735026), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.5773503, -0.5773503, 0.57735026), (-0.51497436, 2.8542892e-8, 0.8572056), (-0.51497436, -0.85720557, -8.155112e-9), (0, -1, 0), (-1.6310224e-8, -0.8572056, 0.5149743), (-0.51497436, -0.85720557, -8.155112e-9), (-1.6310224e-8, -0.8572056, 0.5149743), (-0.5773503, -0.5773503, 0.57735026), (-0.5773503, -0.5773503, 0.57735026), (-1.6310224e-8, -0.8572056, 0.5149743), (-8.155112e-9, -0.5149744, 0.85720557), (-0.5773503, -0.5773503, 0.57735026), (-8.155112e-9, -0.5149744, 0.85720557), (-0.51497436, 2.8542892e-8, 0.8572056), (-0.51497436, 2.8542892e-8, 0.8572056), (-8.155112e-9, -0.5149744, 0.85720557), (0, 0, 1), (0, 0, 1), (-8.155112e-9, -0.5149744, 0.85720557), (0.5149743, 0, 0.8572056), (-8.155112e-9, -0.5149744, 0.85720557), (-1.6310224e-8, -0.8572056, 0.5149743), (0.5773503, -0.5773503, 0.57735026), (-8.155112e-9, -0.5149744, 0.85720557), (0.5773503, -0.5773503, 0.57735026), (0.5149743, 0, 0.8572056), (0.5149743, 0, 0.8572056), (0.5773503, -0.5773503, 0.57735026), (0.8572056, -8.155113e-9, 0.5149743), (-1.6310224e-8, -0.8572056, 0.5149743), (0, -1, 0), (0.51497424, -0.85720557, 2.4465333e-8), (-1.6310224e-8, -0.8572056, 0.5149743), (0.51497424, -0.85720557, 2.4465333e-8), (0.5773503, -0.5773503, 0.57735026), (0.5773503, -0.5773503, 0.57735026), (0.51497424, -0.85720557, 2.4465333e-8), (0.8572056, -0.51497436, -3.262045e-8), (0.5773503, -0.5773503, 0.57735026), (0.8572056, -0.51497436, -3.262045e-8), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, -0.51497436, -3.262045e-8), (1, 0, 0), (0, 0, -1), (2.0387782e-8, 0.5149743, -0.8572056), (0.5149743, -8.155113e-9, -0.8572056), (2.0387782e-8, 0.5149743, -0.8572056), (8.155113e-9, 0.8572056, -0.51497436), (0.5773503, 0.57735026, -0.5773503), (2.0387782e-8, 0.5149743, -0.8572056), (0.5773503, 0.57735026, -0.5773503), (0.5149743, -8.155113e-9, -0.8572056), (0.5149743, -8.155113e-9, -0.8572056), (0.5773503, 0.57735026, -0.5773503), (0.8572056, 0, -0.5149744), (8.155113e-9, 0.8572056, -0.51497436), (0, 1, 0), (0.51497436, 0.85720557, -8.155112e-9), (8.155113e-9, 0.8572056, -0.51497436), (0.51497436, 0.85720557, -8.155112e-9), (0.5773503, 0.57735026, -0.5773503), (0.5773503, 0.57735026, -0.5773503), (0.51497436, 0.85720557, -8.155112e-9), (0.8572056, 0.5149743, -3.262045e-8), (0.5773503, 0.57735026, -0.5773503), (0.8572056, 0.5149743, -3.262045e-8), (0.8572056, 0, -0.5149744), (0.8572056, 0, -0.5149744), (0.8572056, 0.5149743, -3.262045e-8), (1, 0, 0), (0, 0, -1), (-0.51497436, -4.0775565e-9, -0.8572056), (2.0387782e-8, 0.5149743, -0.8572056), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.57735026, 0.57735026, -0.57735026), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.57735026, 0.57735026, -0.57735026), (2.0387782e-8, 0.5149743, -0.8572056), (2.0387782e-8, 0.5149743, -0.8572056), (-0.57735026, 0.57735026, -0.57735026), (8.155113e-9, 0.8572056, -0.51497436), (-0.85720557, -4.0775565e-9, -0.51497436), (-1, 0, 0), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.57735026, 0.57735026, -0.57735026), (-0.57735026, 0.57735026, -0.57735026), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.51497436, 0.8572056, 8.155113e-9), (-0.57735026, 0.57735026, -0.57735026), (-0.51497436, 0.8572056, 8.155113e-9), (8.155113e-9, 0.8572056, -0.51497436), (8.155113e-9, 0.8572056, -0.51497436), (-0.51497436, 0.8572056, 8.155113e-9), (0, 1, 0), (0, 0, -1), (-2.0387782e-8, -0.5149743, -0.8572056), (-0.51497436, -4.0775565e-9, -0.8572056), (-2.0387782e-8, -0.5149743, -0.8572056), (-8.155113e-9, -0.8572056, -0.51497436), (-0.5773503, -0.57735026, -0.5773503), (-2.0387782e-8, -0.5149743, -0.8572056), (-0.5773503, -0.57735026, -0.5773503), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.5773503, -0.57735026, -0.5773503), (-0.85720557, -4.0775565e-9, -0.51497436), (-8.155113e-9, -0.8572056, -0.51497436), (0, -1, 0), (-0.51497436, -0.85720557, -8.155112e-9), (-8.155113e-9, -0.8572056, -0.51497436), (-0.51497436, -0.85720557, -8.155112e-9), (-0.5773503, -0.57735026, -0.5773503), (-0.5773503, -0.57735026, -0.5773503), (-0.51497436, -0.85720557, -8.155112e-9), (-0.8572056, -0.5149743, -3.262045e-8), (-0.5773503, -0.57735026, -0.5773503), (-0.8572056, -0.5149743, -3.262045e-8), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.8572056, -0.5149743, -3.262045e-8), (-1, 0, 0), (0, 0, -1), (0.5149743, -8.155113e-9, -0.8572056), (-2.0387782e-8, -0.5149743, -0.8572056), (0.5149743, -8.155113e-9, -0.8572056), (0.8572056, 0, -0.5149744), (0.57735026, -0.57735026, -0.57735026), (0.5149743, -8.155113e-9, -0.8572056), (0.57735026, -0.57735026, -0.57735026), (-2.0387782e-8, -0.5149743, -0.8572056), (-2.0387782e-8, -0.5149743, -0.8572056), (0.57735026, -0.57735026, -0.57735026), (-8.155113e-9, -0.8572056, -0.51497436), (0.8572056, 0, -0.5149744), (1, 0, 0), (0.8572056, -0.51497436, -3.262045e-8), (0.8572056, 0, -0.5149744), (0.8572056, -0.51497436, -3.262045e-8), (0.57735026, -0.57735026, -0.57735026), (0.57735026, -0.57735026, -0.57735026), (0.8572056, -0.51497436, -3.262045e-8), (0.51497424, -0.85720557, 2.4465333e-8), (0.57735026, -0.57735026, -0.57735026), (0.51497424, -0.85720557, 2.4465333e-8), (-8.155113e-9, -0.8572056, -0.51497436), (-8.155113e-9, -0.8572056, -0.51497436), (0.51497424, -0.85720557, 2.4465333e-8), (0, -1, 0)] ( + interpolation = "faceVarying" + ) + point3f[] points = [(0, 0, 0.5), (0.5, 0, 0), (0, 0.5, 0), (-0.5, 0, 0), (0, -0.5, 0), (0, 0, -0.5), (0.4472136, 0.22360677, 0), (0.22360681, 0.4472136, 0), (0.4472136, 0, 0.22360677), (0.22360681, 0, 0.4472136), (0, 0.4472136, 0.22360677), (0, 0.22360681, 0.4472136), (0.28867516, 0.28867513, 0.28867513), (-0.22360677, 0.4472136, 0), (-0.4472136, 0.22360681, 0), (-0.4472136, 0, 0.22360677), (-0.22360681, 0, 0.4472136), (-0.28867513, 0.28867516, 0.28867513), (-0.4472136, -0.22360677, 0), (-0.22360681, -0.4472136, 0), (0, -0.4472136, 0.22360677), (0, -0.22360681, 0.4472136), (-0.28867516, -0.28867513, 0.28867513), (0.22360677, -0.4472136, 0), (0.4472136, -0.22360681, 0), (0.28867516, -0.28867516, 0.28867513), (0, 0.22360677, -0.4472136), (0, 0.4472136, -0.22360681), (0.22360677, 0, -0.4472136), (0.4472136, 0, -0.22360681), (0.28867513, 0.28867513, -0.28867516), (-0.22360677, 0, -0.4472136), (-0.4472136, 0, -0.22360681), (-0.28867513, 0.28867513, -0.28867516), (0, -0.22360677, -0.4472136), (0, -0.4472136, -0.22360681), (-0.28867513, -0.28867513, -0.28867516), (0.28867513, -0.28867513, -0.28867516)] + texCoord2f[] primvars:st = [(0.5, 0.5), (0.75, 0.5), (0.5, 1), (0.25, 0.5), (0.5, 1), (1, 0.5), (0.75, 0.6475836), (0.75, 0.8524164), (0.6762082, 0.5), (0.5737918, 0.5), (0.5, 0.8524164), (0.5, 0.64758366), (0.625, 0.69591326), (0.25, 0.8524164), (0.25, 0.64758366), (0.3237918, 0.5), (0.4262082, 0.5), (0.375, 0.6959133), (0.25, 0.3524164), (0.25, 0.14758359), (0.5, 0.14758359), (0.5, 0.3524164), (0.375, 0.30408674), (0.75, 0.14758359), (0.75, 0.3524164), (0.625, 0.3040867), (1, 0.6475836), (1, 0.8524164), (0.9262082, 0.5), (0.8237918, 0.5), (0.875, 0.69591326), (0.073791794, 0.5), (0.1762082, 0.5), (0.125, 0.69591326), (1, 0.3524164), (1, 0.14758359), (0.125, 0.30408674), (0.875, 0.30408674), (1.0737917, 0.5), (1.125, 0.69591326), (1.25, 0.8524164), (1.125, 0.30408674), (1.25, 0.14758359)] ( + customData = { + dictionary Maya = { + token name = "map1" + } + } + interpolation = "faceVarying" + ) + int[] primvars:st:indices = [1, 6, 8, 6, 7, 12, 6, 12, 8, 8, 12, 9, 7, 2, 10, 7, 10, 12, 12, 10, 11, 12, 11, 9, 9, 11, 0, 2, 13, 10, 13, 14, 17, 13, 17, 10, 10, 17, 11, 14, 3, 15, 14, 15, 17, 17, 15, 16, 17, 16, 11, 11, 16, 0, 3, 18, 15, 18, 19, 22, 18, 22, 15, 15, 22, 16, 19, 4, 20, 19, 20, 22, 22, 20, 21, 22, 21, 16, 16, 21, 0, 0, 21, 9, 21, 20, 25, 21, 25, 9, 9, 25, 8, 20, 4, 23, 20, 23, 25, 25, 23, 24, 25, 24, 8, 8, 24, 1, 5, 26, 28, 26, 27, 30, 26, 30, 28, 28, 30, 29, 27, 2, 7, 27, 7, 30, 30, 7, 6, 30, 6, 29, 29, 6, 1, 5, 38, 26, 31, 32, 33, 38, 39, 26, 26, 39, 27, 32, 3, 14, 32, 14, 33, 33, 14, 13, 39, 40, 27, 27, 40, 2, 5, 34, 38, 34, 35, 41, 34, 41, 38, 31, 36, 32, 35, 4, 42, 35, 42, 41, 36, 19, 18, 36, 18, 32, 32, 18, 3, 5, 28, 34, 28, 29, 37, 28, 37, 34, 34, 37, 35, 29, 1, 24, 29, 24, 37, 37, 24, 23, 37, 23, 35, 35, 23, 4] + uniform token subdivisionScheme = "none" + double3 xformOp:translate = (1.6945269731517678, 1.2408521489350353, 0.3012844703334947) + uniform token[] xformOpOrder = ["xformOp:translate"] + + def Scope "mtl" + { + def Material "standardSurface2SG" + { + token outputs:mtlx:surface.connect = + + def Shader "surface1" + { + uniform token info:id = "ND_surface" + token inputs:bsdf.connect = + token inputs:edf.connect = + token outputs:out + } + + def Shader "uniform_edf1" + { + uniform token info:id = "ND_uniform_edf" + color3f inputs:color.connect = + token outputs:out + } + + def Shader "fractal3d1" + { + uniform token info:id = "ND_fractal3d_color3" + float3 inputs:amplitude = (0.5, 0.5, 0.5) + color3f outputs:out + } + + def Shader "burley_diffuse_bsdf1" + { + uniform token info:id = "ND_burley_diffuse_bsdf" + color3f inputs:color.connect = + token outputs:out + } + + def Shader "ramplr1" + { + uniform token info:id = "ND_ramplr_color3" + color3f inputs:valuel = (0, 1, 0) + color3f inputs:valuer = (1, 0.5297, 0) + color3f outputs:out + } + } + } +} + +def Mesh "pSphere2" ( + prepend apiSchemas = ["MaterialBindingAPI"] + kind = "component" +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + int[] faceVertexIndices = [1, 6, 8, 6, 7, 12, 6, 12, 8, 8, 12, 9, 7, 2, 10, 7, 10, 12, 12, 10, 11, 12, 11, 9, 9, 11, 0, 2, 13, 10, 13, 14, 17, 13, 17, 10, 10, 17, 11, 14, 3, 15, 14, 15, 17, 17, 15, 16, 17, 16, 11, 11, 16, 0, 3, 18, 15, 18, 19, 22, 18, 22, 15, 15, 22, 16, 19, 4, 20, 19, 20, 22, 22, 20, 21, 22, 21, 16, 16, 21, 0, 0, 21, 9, 21, 20, 25, 21, 25, 9, 9, 25, 8, 20, 4, 23, 20, 23, 25, 25, 23, 24, 25, 24, 8, 8, 24, 1, 5, 26, 28, 26, 27, 30, 26, 30, 28, 28, 30, 29, 27, 2, 7, 27, 7, 30, 30, 7, 6, 30, 6, 29, 29, 6, 1, 5, 31, 26, 31, 32, 33, 31, 33, 26, 26, 33, 27, 32, 3, 14, 32, 14, 33, 33, 14, 13, 33, 13, 27, 27, 13, 2, 5, 34, 31, 34, 35, 36, 34, 36, 31, 31, 36, 32, 35, 4, 19, 35, 19, 36, 36, 19, 18, 36, 18, 32, 32, 18, 3, 5, 28, 34, 28, 29, 37, 28, 37, 34, 34, 37, 35, 29, 1, 24, 29, 24, 37, 37, 24, 23, 37, 23, 35, 35, 23, 4] + rel material:binding = + normal3f[] normals = [(1, 0, 0), (0.8572056, 0.5149743, -3.262045e-8), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, 0.5149743, -3.262045e-8), (0.51497436, 0.85720557, -8.155112e-9), (0.5773503, 0.5773503, 0.57735026), (0.8572056, 0.5149743, -3.262045e-8), (0.5773503, 0.5773503, 0.57735026), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, -8.155113e-9, 0.5149743), (0.5773503, 0.5773503, 0.57735026), (0.5149743, 0, 0.8572056), (0.51497436, 0.85720557, -8.155112e-9), (0, 1, 0), (2.8542896e-8, 0.8572056, 0.51497436), (0.51497436, 0.85720557, -8.155112e-9), (2.8542896e-8, 0.8572056, 0.51497436), (0.5773503, 0.5773503, 0.57735026), (0.5773503, 0.5773503, 0.57735026), (2.8542896e-8, 0.8572056, 0.51497436), (-8.155111e-9, 0.51497436, 0.8572055), (0.5773503, 0.5773503, 0.57735026), (-8.155111e-9, 0.51497436, 0.8572055), (0.5149743, 0, 0.8572056), (0.5149743, 0, 0.8572056), (-8.155111e-9, 0.51497436, 0.8572055), (0, 0, 1), (0, 1, 0), (-0.51497436, 0.8572056, 8.155113e-9), (2.8542896e-8, 0.8572056, 0.51497436), (-0.51497436, 0.8572056, 8.155113e-9), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.57735026, 0.57735026, 0.5773503), (-0.51497436, 0.8572056, 8.155113e-9), (-0.57735026, 0.57735026, 0.5773503), (2.8542896e-8, 0.8572056, 0.51497436), (2.8542896e-8, 0.8572056, 0.51497436), (-0.57735026, 0.57735026, 0.5773503), (-8.155111e-9, 0.51497436, 0.8572055), (-0.85720557, 0.51497436, -1.6310224e-8), (-1, 0, 0), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.57735026, 0.57735026, 0.5773503), (-0.57735026, 0.57735026, 0.5773503), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.51497436, 2.8542892e-8, 0.8572056), (-0.57735026, 0.57735026, 0.5773503), (-0.51497436, 2.8542892e-8, 0.8572056), (-8.155111e-9, 0.51497436, 0.8572055), (-8.155111e-9, 0.51497436, 0.8572055), (-0.51497436, 2.8542892e-8, 0.8572056), (0, 0, 1), (-1, 0, 0), (-0.8572056, -0.5149743, -3.262045e-8), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.8572056, -0.5149743, -3.262045e-8), (-0.51497436, -0.85720557, -8.155112e-9), (-0.5773503, -0.5773503, 0.57735026), (-0.8572056, -0.5149743, -3.262045e-8), (-0.5773503, -0.5773503, 0.57735026), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.8572056, 4.0775565e-9, 0.51497436), (-0.5773503, -0.5773503, 0.57735026), (-0.51497436, 2.8542892e-8, 0.8572056), (-0.51497436, -0.85720557, -8.155112e-9), (0, -1, 0), (-1.6310224e-8, -0.8572056, 0.5149743), (-0.51497436, -0.85720557, -8.155112e-9), (-1.6310224e-8, -0.8572056, 0.5149743), (-0.5773503, -0.5773503, 0.57735026), (-0.5773503, -0.5773503, 0.57735026), (-1.6310224e-8, -0.8572056, 0.5149743), (-8.155112e-9, -0.5149744, 0.85720557), (-0.5773503, -0.5773503, 0.57735026), (-8.155112e-9, -0.5149744, 0.85720557), (-0.51497436, 2.8542892e-8, 0.8572056), (-0.51497436, 2.8542892e-8, 0.8572056), (-8.155112e-9, -0.5149744, 0.85720557), (0, 0, 1), (0, 0, 1), (-8.155112e-9, -0.5149744, 0.85720557), (0.5149743, 0, 0.8572056), (-8.155112e-9, -0.5149744, 0.85720557), (-1.6310224e-8, -0.8572056, 0.5149743), (0.5773503, -0.5773503, 0.57735026), (-8.155112e-9, -0.5149744, 0.85720557), (0.5773503, -0.5773503, 0.57735026), (0.5149743, 0, 0.8572056), (0.5149743, 0, 0.8572056), (0.5773503, -0.5773503, 0.57735026), (0.8572056, -8.155113e-9, 0.5149743), (-1.6310224e-8, -0.8572056, 0.5149743), (0, -1, 0), (0.51497424, -0.85720557, 2.4465333e-8), (-1.6310224e-8, -0.8572056, 0.5149743), (0.51497424, -0.85720557, 2.4465333e-8), (0.5773503, -0.5773503, 0.57735026), (0.5773503, -0.5773503, 0.57735026), (0.51497424, -0.85720557, 2.4465333e-8), (0.8572056, -0.51497436, -3.262045e-8), (0.5773503, -0.5773503, 0.57735026), (0.8572056, -0.51497436, -3.262045e-8), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, -8.155113e-9, 0.5149743), (0.8572056, -0.51497436, -3.262045e-8), (1, 0, 0), (0, 0, -1), (2.0387782e-8, 0.5149743, -0.8572056), (0.5149743, -8.155113e-9, -0.8572056), (2.0387782e-8, 0.5149743, -0.8572056), (8.155113e-9, 0.8572056, -0.51497436), (0.5773503, 0.57735026, -0.5773503), (2.0387782e-8, 0.5149743, -0.8572056), (0.5773503, 0.57735026, -0.5773503), (0.5149743, -8.155113e-9, -0.8572056), (0.5149743, -8.155113e-9, -0.8572056), (0.5773503, 0.57735026, -0.5773503), (0.8572056, 0, -0.5149744), (8.155113e-9, 0.8572056, -0.51497436), (0, 1, 0), (0.51497436, 0.85720557, -8.155112e-9), (8.155113e-9, 0.8572056, -0.51497436), (0.51497436, 0.85720557, -8.155112e-9), (0.5773503, 0.57735026, -0.5773503), (0.5773503, 0.57735026, -0.5773503), (0.51497436, 0.85720557, -8.155112e-9), (0.8572056, 0.5149743, -3.262045e-8), (0.5773503, 0.57735026, -0.5773503), (0.8572056, 0.5149743, -3.262045e-8), (0.8572056, 0, -0.5149744), (0.8572056, 0, -0.5149744), (0.8572056, 0.5149743, -3.262045e-8), (1, 0, 0), (0, 0, -1), (-0.51497436, -4.0775565e-9, -0.8572056), (2.0387782e-8, 0.5149743, -0.8572056), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.57735026, 0.57735026, -0.57735026), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.57735026, 0.57735026, -0.57735026), (2.0387782e-8, 0.5149743, -0.8572056), (2.0387782e-8, 0.5149743, -0.8572056), (-0.57735026, 0.57735026, -0.57735026), (8.155113e-9, 0.8572056, -0.51497436), (-0.85720557, -4.0775565e-9, -0.51497436), (-1, 0, 0), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.57735026, 0.57735026, -0.57735026), (-0.57735026, 0.57735026, -0.57735026), (-0.85720557, 0.51497436, -1.6310224e-8), (-0.51497436, 0.8572056, 8.155113e-9), (-0.57735026, 0.57735026, -0.57735026), (-0.51497436, 0.8572056, 8.155113e-9), (8.155113e-9, 0.8572056, -0.51497436), (8.155113e-9, 0.8572056, -0.51497436), (-0.51497436, 0.8572056, 8.155113e-9), (0, 1, 0), (0, 0, -1), (-2.0387782e-8, -0.5149743, -0.8572056), (-0.51497436, -4.0775565e-9, -0.8572056), (-2.0387782e-8, -0.5149743, -0.8572056), (-8.155113e-9, -0.8572056, -0.51497436), (-0.5773503, -0.57735026, -0.5773503), (-2.0387782e-8, -0.5149743, -0.8572056), (-0.5773503, -0.57735026, -0.5773503), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.51497436, -4.0775565e-9, -0.8572056), (-0.5773503, -0.57735026, -0.5773503), (-0.85720557, -4.0775565e-9, -0.51497436), (-8.155113e-9, -0.8572056, -0.51497436), (0, -1, 0), (-0.51497436, -0.85720557, -8.155112e-9), (-8.155113e-9, -0.8572056, -0.51497436), (-0.51497436, -0.85720557, -8.155112e-9), (-0.5773503, -0.57735026, -0.5773503), (-0.5773503, -0.57735026, -0.5773503), (-0.51497436, -0.85720557, -8.155112e-9), (-0.8572056, -0.5149743, -3.262045e-8), (-0.5773503, -0.57735026, -0.5773503), (-0.8572056, -0.5149743, -3.262045e-8), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.85720557, -4.0775565e-9, -0.51497436), (-0.8572056, -0.5149743, -3.262045e-8), (-1, 0, 0), (0, 0, -1), (0.5149743, -8.155113e-9, -0.8572056), (-2.0387782e-8, -0.5149743, -0.8572056), (0.5149743, -8.155113e-9, -0.8572056), (0.8572056, 0, -0.5149744), (0.57735026, -0.57735026, -0.57735026), (0.5149743, -8.155113e-9, -0.8572056), (0.57735026, -0.57735026, -0.57735026), (-2.0387782e-8, -0.5149743, -0.8572056), (-2.0387782e-8, -0.5149743, -0.8572056), (0.57735026, -0.57735026, -0.57735026), (-8.155113e-9, -0.8572056, -0.51497436), (0.8572056, 0, -0.5149744), (1, 0, 0), (0.8572056, -0.51497436, -3.262045e-8), (0.8572056, 0, -0.5149744), (0.8572056, -0.51497436, -3.262045e-8), (0.57735026, -0.57735026, -0.57735026), (0.57735026, -0.57735026, -0.57735026), (0.8572056, -0.51497436, -3.262045e-8), (0.51497424, -0.85720557, 2.4465333e-8), (0.57735026, -0.57735026, -0.57735026), (0.51497424, -0.85720557, 2.4465333e-8), (-8.155113e-9, -0.8572056, -0.51497436), (-8.155113e-9, -0.8572056, -0.51497436), (0.51497424, -0.85720557, 2.4465333e-8), (0, -1, 0)] ( + interpolation = "faceVarying" + ) + point3f[] points = [(0, 0, 0.5), (0.5, 0, 0), (0, 0.5, 0), (-0.5, 0, 0), (0, -0.5, 0), (0, 0, -0.5), (0.4472136, 0.22360677, 0), (0.22360681, 0.4472136, 0), (0.4472136, 0, 0.22360677), (0.22360681, 0, 0.4472136), (0, 0.4472136, 0.22360677), (0, 0.22360681, 0.4472136), (0.28867516, 0.28867513, 0.28867513), (-0.22360677, 0.4472136, 0), (-0.4472136, 0.22360681, 0), (-0.4472136, 0, 0.22360677), (-0.22360681, 0, 0.4472136), (-0.28867513, 0.28867516, 0.28867513), (-0.4472136, -0.22360677, 0), (-0.22360681, -0.4472136, 0), (0, -0.4472136, 0.22360677), (0, -0.22360681, 0.4472136), (-0.28867516, -0.28867513, 0.28867513), (0.22360677, -0.4472136, 0), (0.4472136, -0.22360681, 0), (0.28867516, -0.28867516, 0.28867513), (0, 0.22360677, -0.4472136), (0, 0.4472136, -0.22360681), (0.22360677, 0, -0.4472136), (0.4472136, 0, -0.22360681), (0.28867513, 0.28867513, -0.28867516), (-0.22360677, 0, -0.4472136), (-0.4472136, 0, -0.22360681), (-0.28867513, 0.28867513, -0.28867516), (0, -0.22360677, -0.4472136), (0, -0.4472136, -0.22360681), (-0.28867513, -0.28867513, -0.28867516), (0.28867513, -0.28867513, -0.28867516)] + texCoord2f[] primvars:st = [(0.5, 0.5), (0.75, 0.5), (0.5, 1), (0.25, 0.5), (0.5, 1), (1, 0.5), (0.75, 0.6475836), (0.75, 0.8524164), (0.6762082, 0.5), (0.5737918, 0.5), (0.5, 0.8524164), (0.5, 0.64758366), (0.625, 0.69591326), (0.25, 0.8524164), (0.25, 0.64758366), (0.3237918, 0.5), (0.4262082, 0.5), (0.375, 0.6959133), (0.25, 0.3524164), (0.25, 0.14758359), (0.5, 0.14758359), (0.5, 0.3524164), (0.375, 0.30408674), (0.75, 0.14758359), (0.75, 0.3524164), (0.625, 0.3040867), (1, 0.6475836), (1, 0.8524164), (0.9262082, 0.5), (0.8237918, 0.5), (0.875, 0.69591326), (0.073791794, 0.5), (0.1762082, 0.5), (0.125, 0.69591326), (1, 0.3524164), (1, 0.14758359), (0.125, 0.30408674), (0.875, 0.30408674), (1.0737917, 0.5), (1.125, 0.69591326), (1.25, 0.8524164), (1.125, 0.30408674), (1.25, 0.14758359)] ( + customData = { + dictionary Maya = { + token name = "map1" + } + } + interpolation = "faceVarying" + ) + int[] primvars:st:indices = [1, 6, 8, 6, 7, 12, 6, 12, 8, 8, 12, 9, 7, 2, 10, 7, 10, 12, 12, 10, 11, 12, 11, 9, 9, 11, 0, 2, 13, 10, 13, 14, 17, 13, 17, 10, 10, 17, 11, 14, 3, 15, 14, 15, 17, 17, 15, 16, 17, 16, 11, 11, 16, 0, 3, 18, 15, 18, 19, 22, 18, 22, 15, 15, 22, 16, 19, 4, 20, 19, 20, 22, 22, 20, 21, 22, 21, 16, 16, 21, 0, 0, 21, 9, 21, 20, 25, 21, 25, 9, 9, 25, 8, 20, 4, 23, 20, 23, 25, 25, 23, 24, 25, 24, 8, 8, 24, 1, 5, 26, 28, 26, 27, 30, 26, 30, 28, 28, 30, 29, 27, 2, 7, 27, 7, 30, 30, 7, 6, 30, 6, 29, 29, 6, 1, 5, 38, 26, 31, 32, 33, 38, 39, 26, 26, 39, 27, 32, 3, 14, 32, 14, 33, 33, 14, 13, 39, 40, 27, 27, 40, 2, 5, 34, 38, 34, 35, 41, 34, 41, 38, 31, 36, 32, 35, 4, 42, 35, 42, 41, 36, 19, 18, 36, 18, 32, 32, 18, 3, 5, 28, 34, 28, 29, 37, 28, 37, 34, 34, 37, 35, 29, 1, 24, 29, 24, 37, 37, 24, 23, 37, 23, 35, 35, 23, 4] + uniform token subdivisionScheme = "none" + double3 xformOp:translate = (1.0485065543167513, 1.7638210594205566, 1.732998156130316) + uniform token[] xformOpOrder = ["xformOp:translate"] + + def Scope "mtl" + { + def Material "standardSurface2SG" + { + token outputs:mtlx:surface.connect = + + def Shader "triplanarprojection1" + { + uniform token info:id = "ND_triplanarprojection_color3" + asset inputs:filex = @textures/grid.png@ + asset inputs:filey = @textures/Mono.png@ + asset inputs:filez = @textures/RGB.png@ + color3f outputs:out + } + + def Shader "standard_surface1" + { + uniform token info:id = "ND_standard_surface_surfaceshader" + color3f inputs:base_color.connect = + token outputs:out + } + } + } +} + diff --git a/test/testSamples/properties/properties.usda b/test/testSamples/properties/properties.usda new file mode 100644 index 0000000000..de028e814e --- /dev/null +++ b/test/testSamples/properties/properties.usda @@ -0,0 +1,91 @@ +#usda 1.0 + +def Sphere "Sphere1" ( + prepend apiSchemas = ["MaterialBindingAPI"] +) +{ + rel material:binding = +} + +def Scope "mtl" +{ + def Material "UsdPreviewSurface1" ( + prepend apiSchemas = ["NodeGraphNodeAPI"] + ) + { + float inputs:clearcoat = 0 ( + sdrMetadata = { + string uiname = "Clearcoat" + string uisoftmax = "1.0" + string uisoftmin = "0.0" + } + ) + float inputs:clearcoatRoughness = 0.01 ( + sdrMetadata = { + string uiname = "Clearcoat Roughness" + string uisoftmax = "1.0" + string uisoftmin = "0.0" + } + ) + float inputs:port = 0 ( + sdrMetadata = { + string uiname = "Port" + } + ) + token outputs:displacement.connect = + float outputs:port.connect = + token outputs:surface.connect = + uniform float2 ui:nodegraph:node:pos = (0.055555556, 0.055555556) + + def Shader "UsdPreviewSurface1" ( + prepend apiSchemas = ["NodeGraphNodeAPI"] + ) + { + uniform token info:id = "UsdPreviewSurface" + float inputs:clearcoat.connect = + token outputs:surface + uniform float2 ui:nodegraph:node:pos = (4.0055556, 0.46666667) + } + + def Shader "surface1" ( + prepend apiSchemas = ["NodeGraphNodeAPI"] + ) + { + uniform token info:id = "ND_surface" + token inputs:bsdf.connect = + uniform float2 ui:nodegraph:node:pos = (6.6200557, 0.98951113) + } + + def NodeGraph "compound" ( + prepend apiSchemas = ["NodeGraphNodeAPI"] + ) + { + float inputs:clearcoat = 0 ( + sdrMetadata = { + string uiname = "Clearcoat" + string uisoftmax = "1.0" + string uisoftmin = "0.0" + } + ) + float inputs:clearcoatRoughness.connect = + float inputs:port.connect = + token outputs:displacement.connect = + float outputs:port + token outputs:surface.connect = + uniform float2 ui:nodegraph:node:pos = (4.1355443, 3.8365722) + + def Shader "UsdPreviewSurface2" ( + prepend apiSchemas = ["NodeGraphNodeAPI"] + ) + { + uniform token info:id = "UsdPreviewSurface" + float inputs:clearcoat.connect = + float inputs:clearcoatRoughness.connect = + token outputs:displacement + token outputs:surface + uniform float2 ui:nodegraph:node:pos = (-0.41053337, 0.045313444) + } + } + } +} +