diff --git a/lib/mayaUsd/commands/CMakeLists.txt b/lib/mayaUsd/commands/CMakeLists.txt index dea33e2a31..40b716f132 100644 --- a/lib/mayaUsd/commands/CMakeLists.txt +++ b/lib/mayaUsd/commands/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources(${PROJECT_NAME} editTargetCommand.cpp layerEditorCommand.cpp layerEditorWindowCommand.cpp + schemaCommand.cpp ) set(HEADERS @@ -21,6 +22,7 @@ set(HEADERS editTargetCommand.h layerEditorCommand.h layerEditorWindowCommand.h + schemaCommand.h ) if(CMAKE_UFE_V3_FEATURES_AVAILABLE) diff --git a/lib/mayaUsd/commands/Readme.md b/lib/mayaUsd/commands/Readme.md index 6390f1edd4..d11751069c 100644 --- a/lib/mayaUsd/commands/Readme.md +++ b/lib/mayaUsd/commands/Readme.md @@ -19,6 +19,7 @@ in this instance for the mayaUsd plug-in. | EditTargetCommand | mayaUsdEditTarget | Command to set or get the edit target | | LayerEditorCommand | mayaUsdLayerEditor | Manipulate layers | | LayerEditorWindowCommand | mayaUsdLayerEditorWindow | Open or manipulate the layer window | +| SchemaCommand | mayaUsdSchema | Manipulate prim schemas | Each base command class is documented in the following sections. @@ -632,6 +633,20 @@ The purpose of this command is to set the current edit target. | `-query` | `-q` | noarg | Retrieve the current edit target | | `-editTarget` | `-et` | string | The name of the target to set with the `-edit` flag | +## `mayaUsdSchema` + +The purpose of this command is to query or apply USD schemas to USD prims. + +### Command Flags + +| Long flag | Short flag | Type | Description | +| --------------------------- | ---------- | -------------- | -------------------------------------------- | +| `-primUfePath` | `-ufe` | string (multi) | The UFE paths to the USD prims | +| `-appliedSchemas` | `-app` | noarg | Query which schemas the prims have in common | +| `-schema` | `-sch` | string | The schema type name to apply to the prims | +| `-instanceName` | `-in` | string | The instance name for multi-apply schema | +| `-singleApplicationSchemas` | `-sas` | noarg | Query the list of known single-apply schemas | +| `-multiApplicationSchemas` | `-mas` | noarg | Query the list of known multi-apply schemas | ## `LayerEditorCommand` diff --git a/lib/mayaUsd/commands/schemaCommand.cpp b/lib/mayaUsd/commands/schemaCommand.cpp new file mode 100644 index 0000000000..dc36328f2e --- /dev/null +++ b/lib/mayaUsd/commands/schemaCommand.cpp @@ -0,0 +1,332 @@ +// +// Copyright 2024 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or dataied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "schemaCommand.h" + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace MAYAUSD_NS_DEF { + +//////////////////////////////////////////////////////////////////////////// +// +// Error message formatting. + +static MString formatMessage(const char* format, const std::string& text) +{ + return PXR_NS::TfStringPrintf(format, text.c_str()).c_str(); +} + +static MString formatMessage(const char* format, PXR_NS::UsdPrim& prim, const std::string& text) +{ + return PXR_NS::TfStringPrintf(format, prim.GetPath().GetString().c_str(), text.c_str()).c_str(); +} + +static std::string formatMessage(const char* format, const Ufe::Path& ufePath) +{ + return PXR_NS::TfStringPrintf(format, Ufe::PathString::string(ufePath).c_str()); +} + +//////////////////////////////////////////////////////////////////////////// +// +// Command name and flags. + +const char SchemaCommand::commandName[] = "mayaUsdSchema"; + +static const char kPrimUfePathsFlag[] = "ufe"; +static const char kPrimUfePathsLongFlag[] = "primUfePath"; +static const char kAppliedSchemasFlag[] = "app"; +static const char kAppliedSchemasLongFlag[] = "appliedSchemas"; +static const char kSchemaFlag[] = "sch"; +static const char kSchemaLongFlag[] = "schema"; +static const char kInstanceNameFlag[] = "in"; +static const char kInstanceNameLongFlag[] = "instanceName"; + +static const char kSingleApplicationFlag[] = "sas"; +static const char kSingleApplicationLongFlag[] = "singleApplicationSchemas"; +static const char kMultiApplicationFlag[] = "mas"; +static const char kMultiApplicationLongFlag[] = "multiApplicationSchemas"; + +//////////////////////////////////////////////////////////////////////////// +// +// Command data and argument parsing to fill that data. + +class SchemaCommand::Data +{ +public: + // Parse the Maya argument list and fill the data with it. + MStatus parseArgs(const MArgList& argList); + + // Convert the list of UFE paths given to the command to the corresponding USD prims. + std::vector getPrims() const; + + // Clears the list of UFE paths given to the command. + // Used to reduce the memory consupmtion once the command has been executed. + void clearPrimPaths() { _primPaths.clear(); } + + // Retrieve the schema name or schema instance name given to the command. + const std::string& getSchema() const { return _schema; } + const std::string& getInstanceName() const { return _instanceName; } + + // Check if the command is a query or which specific type of query. + bool isQuerying() const { return isQueryingAppliedSchemas() || isQueryingKnownSchemas(); } + bool isQueryingKnownSchemas() const + { + return isQueryingSingleAppSchemas() || isQueryingMultiAppSchemas(); + } + bool isQueryingAppliedSchemas() const { return _isQueryingAppliedSchemas; } + bool isQueryingSingleAppSchemas() const { return _singleApplicationSchemas; } + bool isQueryingMultiAppSchemas() const { return _multiApplicationSchemas; } + + // Undo and redo data and implementation. + UsdUfe::UsdUndoableItem& getUsdUndoItem() { return _undoData; } + void undo() { _undoData.undo(); } + void redo() { _undoData.redo(); } + +private: + void parsePrimPaths(const MArgDatabase& argData); + std::string parseStringArg(const MArgDatabase& argData, const char* argFlag); + + std::vector _primPaths; + bool _isQueryingAppliedSchemas { false }; + bool _singleApplicationSchemas { false }; + bool _multiApplicationSchemas { false }; + std::string _schema; + std::string _instanceName; + + UsdUfe::UsdUndoableItem _undoData; +}; + +MStatus SchemaCommand::Data::parseArgs(const MArgList& argList) +{ + MStatus status; + MArgDatabase argData(SchemaCommand::createSyntax(), argList, &status); + if (!status) + return status; + + _isQueryingAppliedSchemas = argData.isFlagSet(kAppliedSchemasFlag); + _schema = parseStringArg(argData, kSchemaFlag); + _instanceName = parseStringArg(argData, kInstanceNameFlag); + _singleApplicationSchemas = argData.isFlagSet(kSingleApplicationFlag); + _multiApplicationSchemas = argData.isFlagSet(kMultiApplicationFlag); + + parsePrimPaths(argData); + + return status; +} + +std::string SchemaCommand::Data::parseStringArg(const MArgDatabase& argData, const char* argFlag) +{ + if (!argData.isFlagSet(argFlag)) + return {}; + + MString stringVal; + argData.getFlagArgument(argFlag, 0, stringVal); + return stringVal.asChar(); +} + +void SchemaCommand::Data::parsePrimPaths(const MArgDatabase& argData) +{ + _primPaths.clear(); + + const unsigned int flagCount = argData.numberOfFlagUses(kPrimUfePathsFlag); + for (unsigned int flagIndex = 0; flagIndex < flagCount; ++flagIndex) { + MArgList argList; + argData.getFlagArgumentList(kPrimUfePathsFlag, flagIndex, argList); + const unsigned int argCount = argList.length(); + for (unsigned int argIndex = 0; argIndex < argCount; ++argIndex) { + const std::string arg = argList.asString(argIndex).asChar(); + if (arg.size() <= 0) + continue; + _primPaths.push_back(Ufe::PathString::path(arg)); + } + } +} + +std::vector SchemaCommand::Data::getPrims() const +{ + std::vector prims; + + for (const Ufe::Path& ufePath : _primPaths) { + PXR_NS::UsdPrim prim = ufe::ufePathToPrim(ufePath); + if (!prim) + throw std::runtime_error(formatMessage("Prim path \"%s\" is invalid", ufePath)); + prims.push_back(prim); + } + + return prims; +} + +//////////////////////////////////////////////////////////////////////////// +// +// Command creation and syntax. + +void* SchemaCommand::creator() { return static_cast(new SchemaCommand()); } + +SchemaCommand::SchemaCommand() + : _data(std::make_unique()) +{ +} + +MSyntax SchemaCommand::createSyntax() +{ + MSyntax syntax; + + syntax.setObjectType(MSyntax::kNone, 0, 0); + + syntax.addFlag(kPrimUfePathsFlag, kPrimUfePathsLongFlag, MSyntax::kString); + syntax.makeFlagMultiUse(kPrimUfePathsFlag); + + syntax.addFlag(kAppliedSchemasFlag, kAppliedSchemasLongFlag); + + syntax.addFlag(kSchemaFlag, kSchemaLongFlag, MSyntax::kString); + syntax.addFlag(kInstanceNameFlag, kInstanceNameLongFlag, MSyntax::kString); + + syntax.addFlag(kSingleApplicationFlag, kSingleApplicationLongFlag); + syntax.addFlag(kMultiApplicationFlag, kMultiApplicationLongFlag); + + return syntax; +} + +bool SchemaCommand::isUndoable() const { return !_data->isQuerying(); } + +//////////////////////////////////////////////////////////////////////////// +// +// Command execution. + +MStatus SchemaCommand::handleAppliedSchemas() +{ + std::set allSchemas = UsdUfe::getPrimsAppliedSchemas(_data->getPrims()); + + MStringArray results; + for (const PXR_NS::TfToken& schema : allSchemas) + results.append(schema.GetString().c_str()); + setResult(results); + + return MS::kSuccess; +} + +MStatus SchemaCommand::handleKnownSchemas() +{ + const UsdUfe::KnownSchemas knownSchemas = UsdUfe::getKnownApplicableSchemas(); + + for (const auto& schema : knownSchemas) { + const bool shouldAppend = schema.second.isMultiApply ? _data->isQueryingMultiAppSchemas() + : _data->isQueryingSingleAppSchemas(); + if (shouldAppend) + appendToResult(schema.second.schemaTypeName.GetString().c_str()); + } + + return MS::kSuccess; +} + +MStatus SchemaCommand::handleApplySchema() +{ + UsdUfe::UsdUndoBlock undoBlock(&_data->getUsdUndoItem()); + + const std::string& schemaName = _data->getSchema(); + if (schemaName.empty()) { + displayError("No schema given to apply to the prims"); + return MS::kInvalidParameter; + } + + auto maybeInfo = UsdUfe::findSchemasByTypeName(PXR_NS::TfToken(schemaName)); + if (!maybeInfo) { + displayError(formatMessage("Cannot find the schema for the type named \"%s\"", schemaName)); + return MS::kInvalidParameter; + } + + const PXR_NS::TfType& schemaType = maybeInfo->schemaType; + + if (maybeInfo->isMultiApply) { + if (_data->getInstanceName().empty()) { + displayError(formatMessage( + "No schema instance name given for the \"%s\" multi-apply schema", schemaName)); + return MS::kInvalidParameter; + } + + for (PXR_NS::UsdPrim& prim : _data->getPrims()) { + if (!UsdUfe::applyMultiSchemaToPrim( + prim, schemaType, PXR_NS::TfToken(_data->getInstanceName()))) { + displayWarning( + formatMessage("Could no apply schema \"%s\" to prim \"%s\"", prim, schemaName)); + } + } + } else { + for (PXR_NS::UsdPrim& prim : _data->getPrims()) { + if (!UsdUfe::applySchemaToPrim(prim, schemaType)) { + displayWarning( + formatMessage("Could no apply schema \"%s\" to prim \"%s\"", prim, schemaName)); + } + } + } + + _data->clearPrimPaths(); + + return MS::kSuccess; +} + +MStatus SchemaCommand::doIt(const MArgList& argList) +{ + try { + setCommandString(commandName); + clearResult(); + + MStatus status = _data->parseArgs(argList); + if (!status) + return status; + + if (_data->isQueryingAppliedSchemas()) + return handleAppliedSchemas(); + + if (_data->isQueryingKnownSchemas()) + return handleKnownSchemas(); + + return handleApplySchema(); + } catch (const std::exception& exc) { + displayError(exc.what()); + return MS::kFailure; + } +} + +MStatus SchemaCommand::redoIt() +{ + _data->redo(); + return MS::kSuccess; +} + +MStatus SchemaCommand::undoIt() +{ + _data->undo(); + return MS::kSuccess; +} + +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/commands/schemaCommand.h b/lib/mayaUsd/commands/schemaCommand.h new file mode 100644 index 0000000000..7435cd7500 --- /dev/null +++ b/lib/mayaUsd/commands/schemaCommand.h @@ -0,0 +1,74 @@ +// +// Copyright 2024 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or dataied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef MAYAUSD_COMMANDS_SCHEMA_COMMAND_H +#define MAYAUSD_COMMANDS_SCHEMA_COMMAND_H + +#include +#include + +#include +#include + +#include +#include + +namespace MAYAUSD_NS_DEF { + +namespace Data { +class SetEditTarget; +} + +class SchemaCommand : public MPxCommand +{ +public: + // plugin registration requirements + MAYAUSD_CORE_PUBLIC + static const char commandName[]; + + MAYAUSD_CORE_PUBLIC + static void* creator(); + + MAYAUSD_CORE_PUBLIC + static MSyntax createSyntax(); + + // MPxCommand callbacks + MAYAUSD_CORE_PUBLIC + MStatus doIt(const MArgList& argList) override; + + MAYAUSD_CORE_PUBLIC + MStatus undoIt() override; + + MAYAUSD_CORE_PUBLIC + MStatus redoIt() override; + + MAYAUSD_CORE_PUBLIC + bool isUndoable() const override; + +private: + SchemaCommand(); + + MStatus handleAppliedSchemas(); + MStatus handleKnownSchemas(); + MStatus handleApplySchema(); + + class Data; + std::unique_ptr _data; +}; + +} // namespace MAYAUSD_NS_DEF + +#endif // MAYAUSD_COMMANDS_SCHEMA_COMMAND_H diff --git a/lib/usdUfe/python/wrapUtils.cpp b/lib/usdUfe/python/wrapUtils.cpp index 82d5af354c..6d1e1d8a73 100644 --- a/lib/usdUfe/python/wrapUtils.cpp +++ b/lib/usdUfe/python/wrapUtils.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,52 @@ bool _isAttributeEditAllowed(const PXR_NS::UsdAttribute& attr) return UsdUfe::isAttributeEditAllowed(attr); } +static boost::python::dict _convertSchemaInfo(const UsdUfe::SchemaInfo& info) +{ + boost::python::dict infoDict; + + infoDict["pluginName"] = info.pluginName; + infoDict["schemaType"] = info.schemaType; + infoDict["schemaTypeName"] = info.schemaTypeName; + infoDict["isMultiApply"] = info.isMultiApply; + + return infoDict; +} + +static boost::python::list _getKnownApplicableSchemas() +{ + boost::python::list schemasList; + + UsdUfe::KnownSchemas knownSchemas = UsdUfe::getKnownApplicableSchemas(); + + for (const auto& info : knownSchemas) + schemasList.append(_convertSchemaInfo(info.second)); + + return schemasList; +} + +static boost::python::dict _findSchemasByTypeName(const PXR_NS::TfToken& schemaTypeName) +{ + auto maybeInfo = UsdUfe::findSchemasByTypeName(schemaTypeName); + if (!maybeInfo) + return {}; + + return _convertSchemaInfo(*maybeInfo); +} + +bool _applySchemaToPrim(PXR_NS::UsdPrim& prim, const PXR_NS::TfType& schemaType) +{ + return UsdUfe::applySchemaToPrim(prim, schemaType); +} + +bool _applyMultiSchemaToPrim( + PXR_NS::UsdPrim& prim, + const PXR_NS::TfType& schemaType, + const PXR_NS::TfToken& instanceName) +{ + return UsdUfe::applyMultiSchemaToPrim(prim, schemaType, instanceName); +} + void wrapUtils() { // Because UsdUfe and UFE have incompatible Python bindings that do not @@ -125,4 +172,8 @@ void wrapUtils() def("getTime", _getTime); def("prettifyName", &UsdUfe::prettifyName); def("isAttributeEditAllowed", _isAttributeEditAllowed); + def("getKnownApplicableSchemas", _getKnownApplicableSchemas); + def("applySchemaToPrim", _applySchemaToPrim); + def("applyMultiSchemaToPrim", _applyMultiSchemaToPrim); + def("findSchemasByTypeName", _findSchemasByTypeName); } diff --git a/lib/usdUfe/utils/CMakeLists.txt b/lib/usdUfe/utils/CMakeLists.txt index e04cc0615f..12379a4302 100644 --- a/lib/usdUfe/utils/CMakeLists.txt +++ b/lib/usdUfe/utils/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources(${PROJECT_NAME} loadRulesText.cpp mergePrims.cpp mergePrimsOptions.cpp + schemas.cpp uiCallback.cpp usdUtils.cpp Utils.cpp @@ -38,6 +39,7 @@ set(HEADERS loadRules.h mergePrims.h mergePrimsOptions.h + schemas.h SIMD.h uiCallback.h usdUtils.h diff --git a/lib/usdUfe/utils/schemas.cpp b/lib/usdUfe/utils/schemas.cpp new file mode 100644 index 0000000000..1ade54dd60 --- /dev/null +++ b/lib/usdUfe/utils/schemas.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2024 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "schemas.h" + +#include +#include +#include + +namespace USDUFE_NS_DEF { + +namespace { + +} // namespace + +KnownSchemas getKnownApplicableSchemas() +{ + KnownSchemas knownSchemas; + + PXR_NS::UsdSchemaRegistry& schemaReg = PXR_NS::UsdSchemaRegistry::GetInstance(); + PXR_NS::PlugRegistry& plugReg = PXR_NS::PlugRegistry::GetInstance(); + + std::set allSchemaTypes; + PXR_NS::TfType::FindByName("UsdAPISchemaBase").GetAllDerivedTypes(&allSchemaTypes); + + for (const PXR_NS::TfType& schemaType : allSchemaTypes) { + if (!schemaReg.IsAppliedAPISchema(schemaType)) + continue; + + PXR_NS::PlugPluginPtr plugin = plugReg.GetPluginForType(schemaType); + if (!plugin) + continue; + + const PXR_NS::TfToken typeName = schemaReg.GetAPISchemaTypeName(schemaType); + const bool isMulti = schemaReg.IsMultipleApplyAPISchema(schemaType); + + SchemaInfo info = { plugin->GetName(), schemaType, typeName, isMulti }; + knownSchemas[typeName] = std::move(info); + } + + return knownSchemas; +} + +std::shared_ptr +findSchemasByTypeName(const PXR_NS::TfToken& schemaTypeName, const KnownSchemas& knownSchemas) +{ + const auto iter = knownSchemas.find(schemaTypeName); + if (iter == knownSchemas.end()) + return std::shared_ptr(); + + return std::make_shared(iter->second); +} + +std::shared_ptr findSchemasByTypeName(const PXR_NS::TfToken& schemaTypeName) +{ + return findSchemasByTypeName(schemaTypeName, UsdUfe::getKnownApplicableSchemas()); +} + +bool applySchemaToPrim(PXR_NS::UsdPrim& prim, const PXR_NS::TfType& schemaType) +{ + return prim.ApplyAPI(schemaType); +} + +bool applySchemaToPrim(PXR_NS::UsdPrim& prim, const SchemaInfo& info) +{ + return applySchemaToPrim(prim, info.schemaType); +} + +bool applyMultiSchemaToPrim( + PXR_NS::UsdPrim& prim, + const PXR_NS::TfType& schemaType, + const PXR_NS::TfToken& instanceName) +{ + return prim.ApplyAPI(schemaType, instanceName); +} + +bool applyMultiSchemaToPrim( + PXR_NS::UsdPrim& prim, + const SchemaInfo& info, + const PXR_NS::TfToken& instanceName) +{ + return applyMultiSchemaToPrim(prim, info.schemaType, instanceName); +} + +std::vector getPrimAppliedSchemas(const PXR_NS::UsdPrim& prim) +{ + const PXR_NS::UsdPrimTypeInfo& info = prim.GetPrimTypeInfo(); + return info.GetAppliedAPISchemas(); +} + +std::set getPrimsAppliedSchemas(const std::vector& prims) +{ + std::set allSchemas; + + for (const PXR_NS::UsdPrim& prim : prims) + for (const PXR_NS::TfToken& schema : getPrimAppliedSchemas(prim)) + allSchemas.insert(schema); + + return allSchemas; +} + +} // namespace USDUFE_NS_DEF \ No newline at end of file diff --git a/lib/usdUfe/utils/schemas.h b/lib/usdUfe/utils/schemas.h new file mode 100644 index 0000000000..8f4c8036d7 --- /dev/null +++ b/lib/usdUfe/utils/schemas.h @@ -0,0 +1,112 @@ +// +// Copyright 2024 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef USDUFE_UTILS_SCHEMAS_H +#define USDUFE_UTILS_SCHEMAS_H + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace USDUFE_NS_DEF { + +/// @brief Describe a single schema. +struct SchemaInfo +{ + std::string pluginName; + PXR_NS::TfType schemaType; + PXR_NS::TfToken schemaTypeName; + bool isMultiApply; +}; + +/// @brief The map of currently known single-apply and multiple-apply schemas indexed by type name. +using KnownSchemas = std::map; + +/// @brief Return the list of currently-known schemas. +/// @return the known schemas. +USDUFE_PUBLIC +KnownSchemas getKnownApplicableSchemas(); + +/// @brief finds a schema by its type name. +/// @param schemaTypeName the schema type to lookup. +/// @return the schema info of the schema, if found. +USDUFE_PUBLIC +std::shared_ptr findSchemasByTypeName(const PXR_NS::TfToken& schemaTypeName); + +/// @brief finds a schema by its type name. +/// @param schemaTypeName the schema type to lookup. +/// @param knownSchemas the list of known schemas. +/// @return the schema info of the schema, if found. +USDUFE_PUBLIC +std::shared_ptr +findSchemasByTypeName(const PXR_NS::TfToken& schemaTypeName, const KnownSchemas& knownSchemas); + +/// @brief apply the given single-apply schema type to the given prim. +/// @param prim the prim to receive the schema. +/// @param schemaType the schema type to apply. +/// @return true if the application succeeded. +USDUFE_PUBLIC +bool applySchemaToPrim(PXR_NS::UsdPrim& prim, const PXR_NS::TfType& schemaType); + +/// @brief apply the given single-apply schema type to the given prim. +/// @param prim the prim to receive the schema. +/// @param info the schema info to apply. +/// @return true if the application succeeded. +USDUFE_PUBLIC +bool applySchemaToPrim(PXR_NS::UsdPrim& prim, const SchemaInfo& info); + +/// @brief apply the given multi-apply schema type to the given prim. +/// @param prim the prim to receive the schema. +/// @param schemaType the schema type to apply. +/// @param instanceName the unique name of the new schema application. +/// @return true if the application succeeded. +USDUFE_PUBLIC +bool applyMultiSchemaToPrim( + PXR_NS::UsdPrim& prim, + const PXR_NS::TfType& schemaType, + const PXR_NS::TfToken& instanceName); + +/// @brief apply the given multi-apply schema type to the given prim. +/// @param prim the prim to receive the schema. +/// @param schemaType the schema type to apply. +/// @param instanceName the unique name of the new schema application. +/// @return true if the application succeeded. +USDUFE_PUBLIC +bool applyMultiSchemaToPrim( + PXR_NS::UsdPrim& prim, + const SchemaInfo& info, + const PXR_NS::TfToken& instanceName); + +/// @brief get all schemas that are applied to the given prim. +/// @return the list of applied schemas. +USDUFE_PUBLIC +std::vector getPrimAppliedSchemas(const PXR_NS::UsdPrim& prim); + +/// @brief get all (the union) the applied schemas that the given prims have. +/// @return the set of applied schemas of all the given prims. +USDUFE_PUBLIC +std::set getPrimsAppliedSchemas(const std::vector& prims); + +} // namespace USDUFE_NS_DEF + +#endif diff --git a/plugin/adsk/plugin/plugin.cpp b/plugin/adsk/plugin/plugin.cpp index 80f03685a1..c2c946bb50 100644 --- a/plugin/adsk/plugin/plugin.cpp +++ b/plugin/adsk/plugin/plugin.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,7 @@ MStatus initializePlugin(MObject obj) registerCommandCheck(plugin); registerCommandCheck(plugin); registerCommandCheck(plugin); + registerCommandCheck(plugin); registerCommandCheck(plugin); #if defined(WANT_QT_BUILD) registerCommandCheck(plugin); @@ -339,6 +341,7 @@ MStatus uninitializePlugin(MObject obj) deregisterCommandCheck(plugin); deregisterCommandCheck(plugin); deregisterCommandCheck(plugin); + deregisterCommandCheck(plugin); deregisterCommandCheck(plugin); #if defined(WANT_QT_BUILD) deregisterCommandCheck(plugin); diff --git a/plugin/adsk/scripts/CMakeLists.txt b/plugin/adsk/scripts/CMakeLists.txt index c37c247beb..9b6819290d 100644 --- a/plugin/adsk/scripts/CMakeLists.txt +++ b/plugin/adsk/scripts/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND scripts_src AEmayaUsdProxyShapeTemplate.mel AETemplateHelpers.py mayaUsdMenu.mel + mayaUsdMenu.py mayaUsd_createStageFromFile.mel mayaUsd_createStageWithNewLayer.py mayaUsd_createStageFromAsset.mel diff --git a/plugin/adsk/scripts/mayaUSDRegisterStrings.py b/plugin/adsk/scripts/mayaUSDRegisterStrings.py index 72ce2cc384..d1fcc3a1a8 100644 --- a/plugin/adsk/scripts/mayaUSDRegisterStrings.py +++ b/plugin/adsk/scripts/mayaUSDRegisterStrings.py @@ -137,6 +137,9 @@ "kShareStageShareable": "Shareable", "kShareStageAnn": "Toggle sharable on and off to sandbox your changes and create a new stage", "kShowArrayAttributes": "Show Array Attributes", + "kAddSchemaMenuItem": "Add Schema", + "kAddSchemaInstanceTitle": "Add Schema Instance", + "kAddSchemaInstanceMesage": "%s Schema Instance Name", "kUSDPointInstancesPickMode_PointInstancer": "Point Instancer", "kUSDPointInstancesPickMode_PointInstancerAnn": "Selection mode for all prims set as point instances.", "kUSDPointInstancesPickMode_Instances": "Instances", diff --git a/plugin/adsk/scripts/mayaUsdMenu.mel b/plugin/adsk/scripts/mayaUsdMenu.mel index b10638cb2a..ed557f423f 100644 --- a/plugin/adsk/scripts/mayaUsdMenu.mel +++ b/plugin/adsk/scripts/mayaUsdMenu.mel @@ -499,6 +499,18 @@ global proc mayaUsdMenu_aeShowMenuCallback(string $aeShowMenu) -command "optionVar -intValue \"mayaUSD_AEShowArrayAttributes\" #1; AEbuildControls;"; } +/////////////////////////////////////////////////////////////////////////////// +// mayaUsdMenu_aeAttributesMenuCallback +// Maya USD Attribute Editor Attributes menu callback. +// Note: Maya is using a post callback to build it's menu. +// The maya's callback delete all menu item each time +// the menu is display, so we also need to create all +// our menu item each time. +global proc mayaUsdMenu_aeAttributesMenuCallback(string $aeAttributeMenu) +{ + python("import mayaUsdMenu; mayaUsdMenu.aeAttributesMenuCallback('''" + $aeAttributeMenu + "''')"); +} + /////////////////////////////////////////////////////////////////////////////// // initCreateMenu // setup the items in Maya's "Create" and "Window" menus @@ -568,6 +580,18 @@ proc termAEShowMenu() removeMenuCallback($aeShowMenu, "mayaUsdMenu_aeShowMenuCallback"); } +proc initAEAttributesMenu() +{ + string $aeAttributesMenu = findAEMenu(uiRes("m_showEditor.kAttributes")); + addMenuCallback($aeAttributesMenu, "mayaUsdMenu_aeAttributesMenuCallback " + $aeAttributesMenu); +} + +proc termAEAttributesMenu() +{ + string $aeAttributesMenu = findAEMenu(uiRes("m_showEditor.kAttributes")); + removeMenuCallback($aeAttributesMenu, "mayaUsdMenu_aeAttributesMenuCallback"); +} + /////////////////////////////////////////////////////////////////////////////// // initPullMenu // Marking menu with USD options for DAG objects @@ -848,6 +872,7 @@ global proc mayaUsdMenu_loadui() { initCreateMenu(); initSelectMenu(); initAEShowMenu(); + initAEAttributesMenu(); // if the current selection kind is not valid, // reset the current selection kind to none. @@ -868,6 +893,7 @@ global proc mayaUsdMenu_unloadui() { termCreateMenu(); termSelectMenu(); termAEShowMenu(); + termAEAttributesMenu(); tearPullMenu(); termOutlinerPopupMenu(); } diff --git a/plugin/adsk/scripts/mayaUsdMenu.py b/plugin/adsk/scripts/mayaUsdMenu.py new file mode 100644 index 0000000000..ef3c8db004 --- /dev/null +++ b/plugin/adsk/scripts/mayaUsdMenu.py @@ -0,0 +1,168 @@ +from maya import cmds + +import ufe +import usdUfe +import mayaUsd.ufe + +from collections import defaultdict +from functools import partial + +from mayaUSDRegisterStrings import getMayaUsdString + +_pluginNiceNames = { + "usdGeom": "Geometry", + "usdLux": "Lighting", + "mayaUsd_Schemas": "Maya Reference", + "usdMedia": "Media", + "usdRender": "Render", + "usdRi": "RenderMan", + "usdShade": "Shading", + "usdSkel": "Skeleton", + "usdUI": "UI", + "usdVol": "Volumes", + "usdProc": "Procedural", + "usdPhysics": "Physics", + "usdArnold": "Arnold", + # Skip legacy AL schemas + "AL_USDMayaSchemasTest": "", + "AL_USDMayaSchemas": "", +} + +def _getNicePluginName(pluginName): + ''' + Convert the plugin name to a nicer name we designed for users. + Return an empty name for plugin we don't want to show. + ''' + return _pluginNiceNames.get(pluginName, pluginName) + + +def _groupSchemasByPlugins(schemas): + ''' + Group schemas by the plugin name that contains them and then + by the schema type name, giving a boolean flag if the schema + is a multi-apply schema. + ''' + schemaByPlugins = defaultdict(dict) + for info in schemas: + pluginName = _getNicePluginName(info['pluginName']) + if not pluginName: + continue + schemaByPlugins[pluginName][info['schemaTypeName']] = info['isMultiApply'] + return schemaByPlugins + + +def _getUsdItemsInSelection(): + ''' + Retrieve the UFE items in the selection that are USD prims. + ''' + ufeSelection = ufe.GlobalSelection.get() + return [item for item in ufeSelection if item.runTimeId() == mayaUsd.ufe.getUsdRunTimeId()] + + +def _askForInstanceName(prettyTypeName): + ''' + Ask the user for the multi-apply schema instance name. + ''' + title = getMayaUsdString("kAddSchemaMenuItem") + message = getMayaUsdString("kAddSchemaInstanceMesage") % prettyTypeName + + result = cmds.promptDialog( + message=message, title=title, + button=('OK', 'Cancel'), + defaultButton='OK', cancelButton='Cancel') + if result != 'OK': + return '' + + return cmds.promptDialog(query=True, text=True) + + +def _applySchemaMenuItemCallback(prettyTypeName, schemaTypeName, isMultiApply, menuItem): + ''' + Apply the given schema to the prims in the current selection. + ''' + primPaths = [ufe.PathString.string(item.path()) for item in _getUsdItemsInSelection()] + if not primPaths: + print('Cannot apply schema "%s": no USD prim are currently selected.' % schemaTypeName) + return + + if isMultiApply: + instanceName = _askForInstanceName(prettyTypeName) + if not instanceName: + return + cmds.mayaUsdSchema(ufe=primPaths, schema=schemaTypeName, instanceName=instanceName) + else: + cmds.mayaUsdSchema(ufe=primPaths, schema=schemaTypeName) + + +def _createApplySchemaCommand(prettyTypeName, schemaTypeName, isMultiApply): + ''' + Create a menuitem callback for teh given argument andwith its metadata updated + to have a nice undo item entry in the Maya Edit menu. + ''' + f = partial(_applySchemaMenuItemCallback, prettyTypeName, schemaTypeName, isMultiApply) + # Update the function metadata so that we get a nice entry in the Maya + # undo menu. + nonBreakSpace = '\xa0' + f.__module__ = ('Apply Schema ' + prettyTypeName).replace(' ', nonBreakSpace) + f.__name__ = '' + return f + + +def _prettifyMenuItem(pluginName, prettyTypeName): + ''' + Make the schema type name more pretty. + ''' + # Note: Python str removesuffix and removeprefix were only added in Python 3.9, + # so we can't use them because we need to support Python 2. + if prettyTypeName.endswith('API'): + prettyTypeName = prettyTypeName[:-3] + + # Note: make sure that the type name is not the same as the plugin name, + # otherwise we would end-up with an empty name! This happens for example + # for the light schema. One of them is called LightAPI... + if prettyTypeName != pluginName and prettyTypeName.startswith(pluginName): + prettyTypeName = prettyTypeName[len(pluginName):] + + prettyTypeName = mayaUsd.ufe.prettifyName(prettyTypeName) + + return prettyTypeName + + +def _createSchemaMenuItem(pluginMenu, pluginName, schemaTypeName, isMultiApply): + ''' + Create a menu item for the schema that will apply it to the selection. + We create them in a function to avoid problems with variable capture + in the lambda. + ''' + prettyTypeName = _prettifyMenuItem(pluginName, schemaTypeName) + suffix = '...' if isMultiApply else '' + cmds.menuItem( + label=prettyTypeName + suffix, parent=pluginMenu, + command=_createApplySchemaCommand(prettyTypeName, schemaTypeName, isMultiApply)) + + +def aeAttributesMenuCallback(aeAttributeMenu): + mayaVersion = cmds.about(apiVersion=True) + cmds.menuItem( + divider=True, dividerLabel=getMayaUsdString("kUniversalSceneDescription"), + parent=aeAttributeMenu) + + hasUSDPrimInSelection = bool(len(_getUsdItemsInSelection())) + addMenu = cmds.menuItem( + label=getMayaUsdString("kAddSchemaMenuItem"), version=mayaVersion//10000, + subMenu=True, parent=aeAttributeMenu, enable=hasUSDPrimInSelection) + + # Make sure plugin names are presented in a predictable order, sorted by their pretty names. + schemaByPlugins = _groupSchemasByPlugins(usdUfe.getKnownApplicableSchemas()) + pluginNames = sorted([(mayaUsd.ufe.prettifyName(name), name) for name in schemaByPlugins.keys()]) + + for prettyPluginName, pluginName in pluginNames: + schemas = schemaByPlugins[pluginName] + pluginMenu = cmds.menuItem(label=prettyPluginName, subMenu=True, parent=addMenu) + + # Make sure the schema names are presented in a predictable order, sorted by their pretty names. + schemaNames = sorted([(_prettifyMenuItem(pluginName, name), name) for name in schemas.keys()]) + + for _, schemaTypeName in schemaNames: + isMultiApply = schemas[schemaTypeName] + _createSchemaMenuItem(pluginMenu, pluginName, schemaTypeName, isMultiApply) diff --git a/test/lib/CMakeLists.txt b/test/lib/CMakeLists.txt index bd81670213..1c465434d1 100644 --- a/test/lib/CMakeLists.txt +++ b/test/lib/CMakeLists.txt @@ -8,6 +8,7 @@ set(TEST_SCRIPT_FILES testMayaUsdProxyAccessor.py testMayaUsdCacheId.py testMayaUsdInfoCommand.py + testMayaUsdSchemaCommand.py ) # Interactive Unit test scripts. diff --git a/test/lib/testMayaUsdSchemaCommand.py b/test/lib/testMayaUsdSchemaCommand.py new file mode 100644 index 0000000000..94e0cb30e1 --- /dev/null +++ b/test/lib/testMayaUsdSchemaCommand.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +# +# Copyright 2024 Autodesk +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import fixturesUtils +import usdUtils + +import unittest + +import mayaUsd.lib + +import maya.cmds as cmds +from maya import standalone + +class MayaUsdSchemaCommandTestCase(unittest.TestCase): + """ Test the MayaUsd Schema command. """ + + @classmethod + def setUpClass(cls): + fixturesUtils.readOnlySetUpClass(__file__) + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + + def testKnownSingleApplySchemasCommand(self): + '''Test the -singleApplicationSchemas flag''' + + schemas = cmds.mayaUsdSchema(singleApplicationSchemas=True) + self.assertIn('LightAPI', schemas) + self.assertIn('MaterialBindingAPI', schemas) + self.assertIn('GeomModelAPI', schemas) + self.assertNotIn('CollectionAPI', schemas) + + schemas = cmds.mayaUsdSchema(sas=True) + self.assertIn('LightAPI', schemas) + self.assertIn('MaterialBindingAPI', schemas) + self.assertIn('GeomModelAPI', schemas) + self.assertNotIn('CollectionAPI', schemas) + + + def testKnownMultiApplySchemasCommand(self): + '''Test the -multiApplicationSchemas flag''' + + schemas = cmds.mayaUsdSchema(multiApplicationSchemas=True) + self.assertNotIn('LightAPI', schemas) + self.assertNotIn('MaterialBindingAPI', schemas) + self.assertNotIn('GeomModelAPI', schemas) + self.assertIn('CollectionAPI', schemas) + + schemas = cmds.mayaUsdSchema(mas=True) + self.assertNotIn('LightAPI', schemas) + self.assertNotIn('MaterialBindingAPI', schemas) + self.assertNotIn('GeomModelAPI', schemas) + self.assertIn('CollectionAPI', schemas) + + + def testApplySchemaCommand(self): + '''Test the -ufe, -schema and -appliedSchemas flags for a single-apply schema''' + + # Create a stage and a prim. + psUfePathStr, psUfePath, _ = usdUtils.createSimpleStage() + stage = mayaUsd.lib.GetPrim(psUfePathStr).GetStage() + + primUsdPath ='/Hello' + primUfePath = psUfePathStr + ',' + primUsdPath + prim = stage.DefinePrim(primUsdPath) + self.assertTrue(prim) + + # Verify the prim has no schema when created. + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertFalse(applied) + + # Add a schema and verify it has been applied. + cmds.mayaUsdSchema(primUfePath=primUfePath, schema='MaterialBindingAPI') + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertTrue(applied) + self.assertEqual(len(applied), 1) + self.assertIn('MaterialBindingAPI', applied) + + # Verify undo removes the schema. + cmds.undo() + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertFalse(applied) + + # Verify redo adds the schema. + cmds.redo() + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertTrue(applied) + self.assertEqual(len(applied), 1) + self.assertIn('MaterialBindingAPI', applied) + + + def testApplySchemaToMultiplePrimsCommand(self): + '''Test the -ufe, -schema and -appliedSchemas flags for a single-apply schema''' + + # Create a stage and a multiple prims. + psUfePathStr, psUfePath, _ = usdUtils.createSimpleStage() + stage = mayaUsd.lib.GetPrim(psUfePathStr).GetStage() + + primUfePaths = [] + for primUsdPath in ['/Hello', '/Bye']: + prim = stage.DefinePrim(primUsdPath) + self.assertTrue(prim) + primUfePaths.append(psUfePathStr + ',' + primUsdPath) + + # Verify the prims have no schema when created. + applied = cmds.mayaUsdSchema(primUfePath=primUfePaths, appliedSchemas=True) + self.assertFalse(applied) + + # Add a schema to both prims and verify it has been applied. + cmds.mayaUsdSchema(primUfePath=primUfePaths, schema='MaterialBindingAPI') + applied = cmds.mayaUsdSchema(primUfePath=primUfePaths, appliedSchemas=True) + self.assertTrue(applied) + self.assertEqual(len(applied), 1) + self.assertIn('MaterialBindingAPI', applied) + + # Verify undo removes the schema. + cmds.undo() + applied = cmds.mayaUsdSchema(primUfePath=primUfePaths, appliedSchemas=True) + self.assertFalse(applied) + + # Verify redo adds the schema. + cmds.redo() + applied = cmds.mayaUsdSchema(primUfePath=primUfePaths, appliedSchemas=True) + self.assertTrue(applied) + self.assertEqual(len(applied), 1) + self.assertIn('MaterialBindingAPI', applied) + + + def testApplyMultiSchemaCommand(self): + '''Test the -ufe, -schema, -instanceName and -appliedSchemas flags for a multi-apply schema''' + + # Create a stage and a prim. + psUfePathStr, psUfePath, _ = usdUtils.createSimpleStage() + stage = mayaUsd.lib.GetPrim(psUfePathStr).GetStage() + + primUsdPath ='/Hello' + primUfePath = psUfePathStr + ',' + primUsdPath + prim = stage.DefinePrim(primUsdPath) + self.assertTrue(prim) + + # Verify the prim has no schema when created. + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertFalse(applied) + + # Incorrectly add a multi-apply schema and verify it has not been applied. + self.assertRaises(RuntimeError, lambda: cmds.mayaUsdSchema(primUfePath=primUfePath, schema='CollectionAPI')) + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertFalse(applied) + + # Add a multi-apply schema and verify it has been applied. + cmds.mayaUsdSchema(primUfePath=primUfePath, schema='CollectionAPI', instanceName='this') + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertTrue(applied) + self.assertEqual(len(applied), 1) + self.assertIn('CollectionAPI:this', applied) + + # Verify undo removes the schema. + cmds.undo() + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertFalse(applied) + + # Verify redo adds the schema. + cmds.redo() + applied = cmds.mayaUsdSchema(primUfePath=primUfePath, appliedSchemas=True) + self.assertTrue(applied) + self.assertEqual(len(applied), 1) + self.assertIn('CollectionAPI:this', applied) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/lib/ufe/CMakeLists.txt b/test/lib/ufe/CMakeLists.txt index 632b2413f1..623f9f5bad 100644 --- a/test/lib/ufe/CMakeLists.txt +++ b/test/lib/ufe/CMakeLists.txt @@ -35,6 +35,7 @@ set(TEST_SCRIPT_FILES testSelection.py testSelectionNotification.py testSelectionByArray.py + testSchemas.py testToggleCommands.py testTransform3dChainOfResponsibility.py testTransform3dTranslate.py diff --git a/test/lib/ufe/testSchemas.py b/test/lib/ufe/testSchemas.py new file mode 100644 index 0000000000..f69dec341d --- /dev/null +++ b/test/lib/ufe/testSchemas.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python + +# +# Copyright 2024 Autodesk +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import fixturesUtils + +from maya import standalone + +import ufe +import mayaUsd.ufe +import usdUfe + +from pxr import Usd + +import unittest +from collections import defaultdict + + +class SchemasTestCase(unittest.TestCase): + '''Test known schemas.''' + + @classmethod + def setUpClass(cls): + fixturesUtils.readOnlySetUpClass(__file__) + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def convertToSchemasByPlugins(self, schemas, multiApply): + schemaByPlugins = defaultdict(dict) + for info in schemas: + self.assertIn('pluginName', info) + self.assertIn('schemaType', info) + self.assertIn('schemaTypeName', info) + self.assertIn('isMultiApply', info) + if multiApply == info['isMultiApply']: + schemaByPlugins[info['pluginName']][info['schemaTypeName']] = info['schemaType'] + return schemaByPlugins + + + def testKnownSchemas(self): + '''Test the getKnownApplicableSchemas function.''' + + # Test on a bunch of arbitrary attributes + knownSchemas = usdUfe.getKnownApplicableSchemas() + + singleSchemaByPlugins = self.convertToSchemasByPlugins(knownSchemas, False) + + self.assertIn('usdGeom', singleSchemaByPlugins) + self.assertIn('GeomModelAPI', singleSchemaByPlugins['usdGeom']) + + self.assertIn('usdShade', singleSchemaByPlugins) + self.assertIn('MaterialBindingAPI', singleSchemaByPlugins['usdShade']) + + self.assertIn('usdLux', singleSchemaByPlugins) + self.assertIn('LightAPI', singleSchemaByPlugins['usdLux']) + + multiSchemaByPlugins = self.convertToSchemasByPlugins(knownSchemas, True) + self.assertIn('usd', multiSchemaByPlugins) + self.assertIn('CollectionAPI', multiSchemaByPlugins['usd']) + + + def testApplySingleApplySchema(self): + '''Test applying a single-apply schema to a prim.''' + stage = Usd.Stage.CreateInMemory() + prim = stage.DefinePrim('/Hello') + self.assertTrue(prim) + + knownSchemas = usdUfe.getKnownApplicableSchemas() + singleSchemaByPlugins = self.convertToSchemasByPlugins(knownSchemas, False) + + self.assertIn('usdGeom', singleSchemaByPlugins) + self.assertIn('GeomModelAPI', singleSchemaByPlugins['usdGeom']) + + modelSchemaType = singleSchemaByPlugins['usdGeom']['GeomModelAPI'] + self.assertTrue(modelSchemaType) + + self.assertFalse(prim.HasAPI(modelSchemaType)) + result = usdUfe.applySchemaToPrim(prim, modelSchemaType) + self.assertTrue(result) + self.assertTrue(prim.HasAPI(modelSchemaType)) + + def testApplyMultiApplySchema(self): + '''Test applying a multi-apply schema to a prim.''' + stage = Usd.Stage.CreateInMemory() + prim = stage.DefinePrim('/Hello') + self.assertTrue(prim) + + knownSchemas = usdUfe.getKnownApplicableSchemas() + multiSchemaByPlugins = self.convertToSchemasByPlugins(knownSchemas, True) + + self.assertIn('usd', multiSchemaByPlugins) + self.assertIn('CollectionAPI', multiSchemaByPlugins['usd']) + + collectionSchemaType = multiSchemaByPlugins['usd']['CollectionAPI'] + self.assertTrue(collectionSchemaType) + + instanceName = 'mine' + self.assertFalse(prim.HasAPI(collectionSchemaType)) + self.assertFalse(prim.HasAPI(collectionSchemaType, instanceName=instanceName)) + result = usdUfe.applyMultiSchemaToPrim(prim, collectionSchemaType, instanceName) + self.assertTrue(result) + self.assertTrue(prim.HasAPI(collectionSchemaType)) + self.assertTrue(prim.HasAPI(collectionSchemaType, instanceName=instanceName)) + + def testFindSchemaInfo(self): + '''Test that findSchemasByTypeName works.''' + unknown = usdUfe.findSchemasByTypeName('unknown') + self.assertFalse(unknown) + + collInfo = usdUfe.findSchemasByTypeName('CollectionAPI') + self.assertTrue(collInfo) + + self.assertIn('pluginName', collInfo) + self.assertIn('schemaType', collInfo) + self.assertIn('schemaTypeName', collInfo) + self.assertIn('isMultiApply', collInfo) + + self.assertEqual(collInfo['pluginName'], 'usd') + self.assertTrue(collInfo['schemaType']) + self.assertEqual(collInfo['schemaTypeName'], 'CollectionAPI') + self.assertEqual(collInfo['isMultiApply'], True) + + +if __name__ == '__main__': + unittest.main(verbosity=2)