|
| 1 | +// |
| 2 | +// Copyright 2024 Autodesk |
| 3 | +// |
| 4 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +// you may not use this file except in compliance with the License. |
| 6 | +// You may obtain a copy of the License at |
| 7 | +// |
| 8 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +// |
| 10 | +// Unless required by applicable law or agreed to in writing, software |
| 11 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or dataied. |
| 13 | +// See the License for the specific language governing permissions and |
| 14 | +// limitations under the License. |
| 15 | +// |
| 16 | + |
| 17 | +#include "schemaCommand.h" |
| 18 | + |
| 19 | +#include <mayaUsd/ufe/Utils.h> |
| 20 | + |
| 21 | +#include <usdUfe/undo/UsdUndoBlock.h> |
| 22 | +#include <usdUfe/undo/UsdUndoableItem.h> |
| 23 | +#include <usdUfe/utils/schemas.h> |
| 24 | + |
| 25 | +#include <pxr/base/tf/stringUtils.h> |
| 26 | +#include <pxr/usd/usd/prim.h> |
| 27 | +#include <pxr/usd/usd/stage.h> |
| 28 | + |
| 29 | +#include <maya/MArgDatabase.h> |
| 30 | +#include <maya/MArgList.h> |
| 31 | +#include <maya/MGlobal.h> |
| 32 | +#include <maya/MStringArray.h> |
| 33 | +#include <maya/MSyntax.h> |
| 34 | +#include <ufe/path.h> |
| 35 | +#include <ufe/pathString.h> |
| 36 | + |
| 37 | +namespace MAYAUSD_NS_DEF { |
| 38 | + |
| 39 | +//////////////////////////////////////////////////////////////////////////// |
| 40 | +// |
| 41 | +// Error message formatting. |
| 42 | + |
| 43 | +static MString formatMessage(const char* format, const std::string& text) |
| 44 | +{ |
| 45 | + return PXR_NS::TfStringPrintf(format, text.c_str()).c_str(); |
| 46 | +} |
| 47 | + |
| 48 | +static MString formatMessage(const char* format, PXR_NS::UsdPrim& prim, const std::string& text) |
| 49 | +{ |
| 50 | + return PXR_NS::TfStringPrintf(format, prim.GetPath().GetString().c_str(), text.c_str()).c_str(); |
| 51 | +} |
| 52 | + |
| 53 | +static std::string formatMessage(const char* format, const Ufe::Path& ufePath) |
| 54 | +{ |
| 55 | + return PXR_NS::TfStringPrintf(format, Ufe::PathString::string(ufePath).c_str()); |
| 56 | +} |
| 57 | + |
| 58 | +//////////////////////////////////////////////////////////////////////////// |
| 59 | +// |
| 60 | +// Command name and flags. |
| 61 | + |
| 62 | +const char SchemaCommand::commandName[] = "mayaUsdSchema"; |
| 63 | + |
| 64 | +static const char kAppliedSchemasFlag[] = "app"; |
| 65 | +static const char kAppliedSchemasLongFlag[] = "appliedSchemas"; |
| 66 | +static const char kSchemaFlag[] = "sch"; |
| 67 | +static const char kSchemaLongFlag[] = "schema"; |
| 68 | +static const char kInstanceNameFlag[] = "in"; |
| 69 | +static const char kInstanceNameLongFlag[] = "instanceName"; |
| 70 | + |
| 71 | +static const char kSingleApplicationFlag[] = "sas"; |
| 72 | +static const char kSingleApplicationLongFlag[] = "singleApplicationSchemas"; |
| 73 | +static const char kMultiApplicationFlag[] = "mas"; |
| 74 | +static const char kMultiApplicationLongFlag[] = "multiApplicationSchemas"; |
| 75 | + |
| 76 | +//////////////////////////////////////////////////////////////////////////// |
| 77 | +// |
| 78 | +// Command data and argument parsing to fill that data. |
| 79 | + |
| 80 | +class SchemaCommand::Data |
| 81 | +{ |
| 82 | +public: |
| 83 | + // Parse the Maya argument list and fill the data with it. |
| 84 | + MStatus parseArgs(const MArgList& argList); |
| 85 | + |
| 86 | + // Convert the list of UFE paths given to the command to the corresponding USD prims. |
| 87 | + std::vector<PXR_NS::UsdPrim> getPrims() const; |
| 88 | + |
| 89 | + // Clears the list of UFE paths given to the command. |
| 90 | + // Used to reduce the memory consupmtion once the command has been executed. |
| 91 | + void clearPrimPaths() { _primPaths.clear(); } |
| 92 | + |
| 93 | + // Retrieve the schema name or schema instance name given to the command. |
| 94 | + const std::string& getSchema() const { return _schema; } |
| 95 | + const std::string& getInstanceName() const { return _instanceName; } |
| 96 | + |
| 97 | + // Check if the command is a query or which specific type of query. |
| 98 | + bool isQuerying() const { return isQueryingAppliedSchemas() || isQueryingKnownSchemas(); } |
| 99 | + bool isQueryingKnownSchemas() const |
| 100 | + { |
| 101 | + return isQueryingSingleAppSchemas() || isQueryingMultiAppSchemas(); |
| 102 | + } |
| 103 | + bool isQueryingAppliedSchemas() const { return _isQueryingAppliedSchemas; } |
| 104 | + bool isQueryingSingleAppSchemas() const { return _singleApplicationSchemas; } |
| 105 | + bool isQueryingMultiAppSchemas() const { return _multiApplicationSchemas; } |
| 106 | + |
| 107 | + // Undo and redo data and implementation. |
| 108 | + UsdUfe::UsdUndoableItem& getUsdUndoItem() { return _undoData; } |
| 109 | + void undo() { _undoData.undo(); } |
| 110 | + void redo() { _undoData.redo(); } |
| 111 | + |
| 112 | +private: |
| 113 | + void parsePrimPaths(MArgDatabase& argData); |
| 114 | + std::string parseStringArg(MArgDatabase& argData, const char* argFlag); |
| 115 | + |
| 116 | + std::vector<Ufe::Path> _primPaths; |
| 117 | + bool _isQueryingAppliedSchemas { false }; |
| 118 | + bool _singleApplicationSchemas { false }; |
| 119 | + bool _multiApplicationSchemas { false }; |
| 120 | + std::string _schema; |
| 121 | + std::string _instanceName; |
| 122 | + |
| 123 | + UsdUfe::UsdUndoableItem _undoData; |
| 124 | +}; |
| 125 | + |
| 126 | +MStatus SchemaCommand::Data::parseArgs(const MArgList& argList) |
| 127 | +{ |
| 128 | + MStatus status; |
| 129 | + MArgDatabase argData(SchemaCommand::createSyntax(), argList, &status); |
| 130 | + if (!status) |
| 131 | + return status; |
| 132 | + |
| 133 | + _isQueryingAppliedSchemas = argData.isFlagSet(kAppliedSchemasFlag); |
| 134 | + _schema = parseStringArg(argData, kSchemaFlag); |
| 135 | + _instanceName = parseStringArg(argData, kInstanceNameFlag); |
| 136 | + _singleApplicationSchemas = argData.isFlagSet(kSingleApplicationFlag); |
| 137 | + _multiApplicationSchemas = argData.isFlagSet(kMultiApplicationFlag); |
| 138 | + |
| 139 | + parsePrimPaths(argData); |
| 140 | + |
| 141 | + return status; |
| 142 | +} |
| 143 | + |
| 144 | +std::string SchemaCommand::Data::parseStringArg(MArgDatabase& argData, const char* argFlag) |
| 145 | +{ |
| 146 | + if (!argData.isFlagSet(argFlag)) |
| 147 | + return {}; |
| 148 | + |
| 149 | + MString stringVal; |
| 150 | + argData.getFlagArgument(argFlag, 0, stringVal); |
| 151 | + return stringVal.asChar(); |
| 152 | +} |
| 153 | + |
| 154 | +void SchemaCommand::Data::parsePrimPaths(MArgDatabase& argData) |
| 155 | +{ |
| 156 | + _primPaths.clear(); |
| 157 | + |
| 158 | + MStringArray ufePathArray; |
| 159 | + argData.getObjects(ufePathArray); |
| 160 | + const unsigned int pathCount = ufePathArray.length(); |
| 161 | + for (unsigned int index = 0; index < pathCount; ++index) { |
| 162 | + const std::string arg = ufePathArray[index].asChar(); |
| 163 | + if (arg.size() <= 0) |
| 164 | + continue; |
| 165 | + _primPaths.push_back(Ufe::PathString::path(arg)); |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +std::vector<PXR_NS::UsdPrim> SchemaCommand::Data::getPrims() const |
| 170 | +{ |
| 171 | + std::vector<PXR_NS::UsdPrim> prims; |
| 172 | + |
| 173 | + for (const Ufe::Path& ufePath : _primPaths) { |
| 174 | + PXR_NS::UsdPrim prim = ufe::ufePathToPrim(ufePath); |
| 175 | + if (!prim) |
| 176 | + throw std::runtime_error(formatMessage("Prim path \"%s\" is invalid", ufePath)); |
| 177 | + prims.push_back(prim); |
| 178 | + } |
| 179 | + |
| 180 | + return prims; |
| 181 | +} |
| 182 | + |
| 183 | +//////////////////////////////////////////////////////////////////////////// |
| 184 | +// |
| 185 | +// Command creation and syntax. |
| 186 | + |
| 187 | +void* SchemaCommand::creator() { return static_cast<MPxCommand*>(new SchemaCommand()); } |
| 188 | + |
| 189 | +SchemaCommand::SchemaCommand() |
| 190 | + : _data(std::make_unique<SchemaCommand::Data>()) |
| 191 | +{ |
| 192 | +} |
| 193 | + |
| 194 | +MSyntax SchemaCommand::createSyntax() |
| 195 | +{ |
| 196 | + MSyntax syntax; |
| 197 | + |
| 198 | + syntax.setObjectType(MSyntax::kStringObjects); |
| 199 | + |
| 200 | + syntax.addFlag(kAppliedSchemasFlag, kAppliedSchemasLongFlag); |
| 201 | + |
| 202 | + syntax.addFlag(kSchemaFlag, kSchemaLongFlag, MSyntax::kString); |
| 203 | + syntax.addFlag(kInstanceNameFlag, kInstanceNameLongFlag, MSyntax::kString); |
| 204 | + |
| 205 | + syntax.addFlag(kSingleApplicationFlag, kSingleApplicationLongFlag); |
| 206 | + syntax.addFlag(kMultiApplicationFlag, kMultiApplicationLongFlag); |
| 207 | + |
| 208 | + return syntax; |
| 209 | +} |
| 210 | + |
| 211 | +bool SchemaCommand::isUndoable() const { return !_data->isQuerying(); } |
| 212 | + |
| 213 | +//////////////////////////////////////////////////////////////////////////// |
| 214 | +// |
| 215 | +// Command execution. |
| 216 | + |
| 217 | +MStatus SchemaCommand::handleAppliedSchemas() |
| 218 | +{ |
| 219 | + std::set<PXR_NS::TfToken> allSchemas = UsdUfe::getPrimsAppliedSchemas(_data->getPrims()); |
| 220 | + |
| 221 | + MStringArray results; |
| 222 | + for (const PXR_NS::TfToken& schema : allSchemas) |
| 223 | + results.append(schema.GetString().c_str()); |
| 224 | + setResult(results); |
| 225 | + |
| 226 | + return MS::kSuccess; |
| 227 | +} |
| 228 | + |
| 229 | +MStatus SchemaCommand::handleKnownSchemas() |
| 230 | +{ |
| 231 | + const UsdUfe::KnownSchemas knownSchemas = UsdUfe::getKnownApplicableSchemas(); |
| 232 | + |
| 233 | + for (const auto& schema : knownSchemas) { |
| 234 | + const bool shouldAppend = schema.second.isMultiApply ? _data->isQueryingMultiAppSchemas() |
| 235 | + : _data->isQueryingSingleAppSchemas(); |
| 236 | + if (shouldAppend) |
| 237 | + appendToResult(schema.second.schemaTypeName.GetString().c_str()); |
| 238 | + } |
| 239 | + |
| 240 | + return MS::kSuccess; |
| 241 | +} |
| 242 | + |
| 243 | +MStatus SchemaCommand::handleApplySchema() |
| 244 | +{ |
| 245 | + UsdUfe::UsdUndoBlock undoBlock(&_data->getUsdUndoItem()); |
| 246 | + |
| 247 | + const std::string& schemaName = _data->getSchema(); |
| 248 | + if (schemaName.empty()) { |
| 249 | + displayError("No schema given to apply to the prims"); |
| 250 | + return MS::kInvalidParameter; |
| 251 | + } |
| 252 | + |
| 253 | + auto maybeInfo = UsdUfe::findSchemasByTypeName(PXR_NS::TfToken(schemaName)); |
| 254 | + if (!maybeInfo) { |
| 255 | + displayError(formatMessage("Cannot find the schema for the type named \"%s\"", schemaName)); |
| 256 | + return MS::kInvalidParameter; |
| 257 | + } |
| 258 | + |
| 259 | + const PXR_NS::TfType& schemaType = maybeInfo->schemaType; |
| 260 | + |
| 261 | + if (maybeInfo->isMultiApply) { |
| 262 | + if (_data->getInstanceName().empty()) { |
| 263 | + displayError(formatMessage( |
| 264 | + "No schema instance name given for the \"%s\" multi-apply schema", schemaName)); |
| 265 | + return MS::kInvalidParameter; |
| 266 | + } |
| 267 | + |
| 268 | + for (PXR_NS::UsdPrim& prim : _data->getPrims()) { |
| 269 | + if (!UsdUfe::applyMultiSchemaToPrim( |
| 270 | + prim, schemaType, PXR_NS::TfToken(_data->getInstanceName()))) { |
| 271 | + displayWarning( |
| 272 | + formatMessage("Could no apply schema \"%s\" to prim \"%s\"", prim, schemaName)); |
| 273 | + } |
| 274 | + } |
| 275 | + } else { |
| 276 | + for (PXR_NS::UsdPrim& prim : _data->getPrims()) { |
| 277 | + if (!UsdUfe::applySchemaToPrim(prim, schemaType)) { |
| 278 | + displayWarning( |
| 279 | + formatMessage("Could no apply schema \"%s\" to prim \"%s\"", prim, schemaName)); |
| 280 | + } |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + _data->clearPrimPaths(); |
| 285 | + |
| 286 | + return MS::kSuccess; |
| 287 | +} |
| 288 | + |
| 289 | +MStatus SchemaCommand::doIt(const MArgList& argList) |
| 290 | +{ |
| 291 | + try { |
| 292 | + setCommandString(commandName); |
| 293 | + clearResult(); |
| 294 | + |
| 295 | + MStatus status = _data->parseArgs(argList); |
| 296 | + if (!status) |
| 297 | + return status; |
| 298 | + |
| 299 | + if (_data->isQueryingAppliedSchemas()) |
| 300 | + return handleAppliedSchemas(); |
| 301 | + |
| 302 | + if (_data->isQueryingKnownSchemas()) |
| 303 | + return handleKnownSchemas(); |
| 304 | + |
| 305 | + return handleApplySchema(); |
| 306 | + } catch (const std::exception& exc) { |
| 307 | + displayError(exc.what()); |
| 308 | + return MS::kFailure; |
| 309 | + } |
| 310 | +} |
| 311 | + |
| 312 | +MStatus SchemaCommand::redoIt() |
| 313 | +{ |
| 314 | + _data->redo(); |
| 315 | + return MS::kSuccess; |
| 316 | +} |
| 317 | + |
| 318 | +MStatus SchemaCommand::undoIt() |
| 319 | +{ |
| 320 | + _data->undo(); |
| 321 | + return MS::kSuccess; |
| 322 | +} |
| 323 | + |
| 324 | +} // namespace MAYAUSD_NS_DEF |
0 commit comments