Skip to content

Commit

Permalink
EMSUSD-872 - Adds the ability to System-lock a layer
Browse files Browse the repository at this point in the history
  • Loading branch information
AramAzhari-adsk committed Feb 15, 2024
1 parent 574260d commit 00346db
Show file tree
Hide file tree
Showing 22 changed files with 370 additions and 54 deletions.
6 changes: 6 additions & 0 deletions lib/mayaUsd/commands/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ The purpose of this command is edit layers.
| `-addAnonymous` | `-aa` | string | Add an anonynous layer at the top of the stack, returns it |
| `-insertSubPath` | `-is` | int string | Insert a sub layer path at a given index |
| `-muteLayer` | `-mt` | bool string | Mute or unmute the named layer |
| `-lockLayer` | `-lk` | int string | Lock, System-Lock or unlock a layer. `0` = Unlocked, `1` = Locked and `2` = System-Locked |
| `-replaceSubPath` | `-rp` | string string | Replaces a path in the layer stack |
| `-removeSubPath` | `-rs` | int string | Remove a sub layer at a given index |
Expand Down Expand Up @@ -660,6 +661,11 @@ The purpose of this command is to control the layer editor window.
| `-layerIsMuted` | `-mu` | Query if the layer itself is muted |
| `-layerIsReadOnly` | `-r` | Query if the layer or any parent is read only |
| `-muteLayer` | `-mt` | Toggle the muting of a layer |
| `-layerAppearsLocked` | `-al` | Query if the layer's parent is locked |
| `-layerIsLocked` | `-lo` | Query if the layer itself is locked |
| `-layerAppearsSystemLocked` | `-as` | Query if the layer's parent is system-locked |
| `-layerIsSystemLocked` | `-ls` | Query if the layer itself is system-locked |
| `-lockLayer` | `-lk` | Lock, System-Lock or unlock a layer. `0` = Unlocked, `1` = Locked and `2` = System-Locked |
| `-layerNeedsSaving` | `-ns` | Query if the layer is dirty or anonymous |
| `-printLayer` | `-pl` | Print the layer to the script editor output |
| `-proxyShape` | `-ps` | Query the proxyShape path or sets the selected shape by its path. Takes the path as argument |
Expand Down
2 changes: 2 additions & 0 deletions lib/mayaUsd/commands/abstractLayerEditorWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class MAYAUSD_CORE_PUBLIC AbstractLayerEditorWindow
virtual bool layerIsMuted() = 0;
virtual bool layerAppearsLocked() = 0;
virtual bool layerIsLocked() = 0;
virtual bool layerAppearsSystemLocked() = 0;
virtual bool layerIsSystemLocked() = 0;
virtual bool layerIsReadOnly() = 0;
virtual std::string proxyShapeName() const = 0;

Expand Down
40 changes: 31 additions & 9 deletions lib/mayaUsd/commands/layerEditorCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "layerEditorCommand.h"

#include <mayaUsd/ufe/Global.h>
#include <mayaUsd/utils/layerLocking.h>
#include <mayaUsd/utils/layerMuting.h>
#include <mayaUsd/utils/query.h>
#include <mayaUsd/utils/stageCache.h>
Expand Down Expand Up @@ -59,6 +60,8 @@ const char kMuteLayerFlag[] = "mt";
const char kMuteLayerFlagL[] = "muteLayer";
const char kLockLayerFlag[] = "lk";
const char kLockLayerFlagL[] = "lockLayer";
const char kSystemLockLayerFlag[] = "sk";
const char kSystemLockLayerFlagL[] = "SystemLockLayer";

} // namespace

Expand Down Expand Up @@ -563,7 +566,7 @@ class BackupLayerBase : public BaseCmd
}

// Note: backup the edit targets after the layer is cleared because we use
// the fact that a stage edit target is now invalid to decice to backup
// the fact that a stage edit target is now invalid to decide to backup
// that edit target.
backupEditTargets(layer);

Expand Down Expand Up @@ -789,12 +792,22 @@ class LockLayer : public BaseCmd
auto stage = getStage();
if (!stage)
return false;
if (_lockIt) {
if (_systemLock) {
saveSelection();
layer->SetPermissionToSave(false);
layer->SetPermissionToEdit(false);
MayaUsd::addSystemLockedLayer(layer);
} else if (_lockIt) {
saveSelection();
layer->SetPermissionToEdit(false);
layer->SetPermissionToSave(true);
MayaUsd::removeSystemLockedLayer(layer);

} else {
saveSelection();
layer->SetPermissionToEdit(true);
layer->SetPermissionToSave(true);
MayaUsd::removeSystemLockedLayer(layer);
}

return true;
Expand All @@ -805,11 +818,15 @@ class LockLayer : public BaseCmd
auto stage = getStage();
if (!stage)
return false;
if (_lockIt) {
if (_systemLock) {
layer->SetPermissionToEdit(true);
layer->SetPermissionToSave(true);
MayaUsd::removeSystemLockedLayer(layer);
restoreSelection();
} else {
layer->SetPermissionToEdit(false);
} else if (_lockIt) {
layer->SetPermissionToEdit(true);
layer->SetPermissionToSave(true);
MayaUsd::removeSystemLockedLayer(layer);
restoreSelection();
}

Expand All @@ -818,6 +835,7 @@ class LockLayer : public BaseCmd

std::string _proxyShapePath;
bool _lockIt = true;
bool _systemLock = false;

private:
UsdStageWeakPtr getStage()
Expand Down Expand Up @@ -949,7 +967,7 @@ MSyntax LayerEditorCommand::createSyntax()
syntax.makeFlagMultiUse(kAddAnonSublayerFlag);
// paramter: proxy shape name
syntax.addFlag(kMuteLayerFlag, kMuteLayerFlagL, MSyntax::kBoolean, MSyntax::kString);
syntax.addFlag(kLockLayerFlag, kLockLayerFlagL, MSyntax::kBoolean, MSyntax::kString);
syntax.addFlag(kLockLayerFlag, kLockLayerFlagL, MSyntax::kLong, MSyntax::kString);

return syntax;
}
Expand Down Expand Up @@ -1091,8 +1109,11 @@ MStatus LayerEditorCommand::parseArgs(const MArgList& argList)
_subCommands.push_back(std::move(cmd));
}
if (argParser.isFlagSet(kLockLayerFlag)) {
bool lockIt = true;
argParser.getFlagArgument(kLockLayerFlag, 0, lockIt);
int lockValue = 0;
// 0 = Unlocked
// 1 = Locked
// 2 = SystemLocked
argParser.getFlagArgument(kLockLayerFlag, 0, lockValue);

MString proxyShapeName;
argParser.getFlagArgument(kLockLayerFlag, 1, proxyShapeName);
Expand All @@ -1105,7 +1126,8 @@ MStatus LayerEditorCommand::parseArgs(const MArgList& argList)
}

auto cmd = std::make_shared<Impl::LockLayer>();
cmd->_lockIt = lockIt;
cmd->_lockIt = lockValue == 1 ? true : false;
cmd->_systemLock = lockValue == 2 ? true : false;
cmd->_proxyShapePath = proxyShapeName.asChar();
_subCommands.push_back(std::move(cmd));
}
Expand Down
6 changes: 6 additions & 0 deletions lib/mayaUsd/commands/layerEditorWindowCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ DEF_FLAG(mu, layerIsMuted)
DEF_FLAG(r, layerIsReadOnly)
DEF_FLAG(al, layerAppearsLocked)
DEF_FLAG(lo, layerIsLocked)
DEF_FLAG(as, layerAppearsSystemLocked)
DEF_FLAG(ls, layerIsSystemLocked)

// edit flags
DEF_FLAG(rs, removeSubLayer)
Expand Down Expand Up @@ -130,6 +132,8 @@ MSyntax LayerEditorWindowCommand::createSyntax()
ADD_FLAG(layerIsReadOnly);
ADD_FLAG(layerAppearsLocked);
ADD_FLAG(layerIsLocked);
ADD_FLAG(layerAppearsSystemLocked);
ADD_FLAG(layerIsSystemLocked);

ADD_FLAG(removeSubLayer);
ADD_FLAG(saveEdits);
Expand Down Expand Up @@ -325,6 +329,8 @@ MStatus LayerEditorWindowCommand::handleQueries(
HANDLE_Q_FLAG(layerIsReadOnly)
HANDLE_Q_FLAG(layerAppearsLocked)
HANDLE_Q_FLAG(layerIsLocked)
HANDLE_Q_FLAG(layerAppearsSystemLocked)
HANDLE_Q_FLAG(layerIsSystemLocked)

if (argParser.isFlagSet(FLAG(proxyShape)) && argParser.isQuery()) {
setResult(layerEditor->proxyShapeName().c_str());
Expand Down
2 changes: 2 additions & 0 deletions lib/mayaUsd/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ target_sources(${PROJECT_NAME}
dynamicAttribute.cpp
editability.cpp
json.cpp
layerLocking.cpp
layerMuting.cpp
layers.cpp
loadRulesAttribute.cpp
Expand Down Expand Up @@ -45,6 +46,7 @@ set(HEADERS
editability.h
hash.h
json.h
layerLocking.h
layerMuting.h
layers.h
loadRules.h
Expand Down
100 changes: 100 additions & 0 deletions lib/mayaUsd/utils/layerLocking.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// 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 "layerLocking.h"

#include <mayaUsd/listeners/notice.h>

#include <pxr/base/tf/weakBase.h>

namespace MAYAUSD_NS_DEF {

namespace {

// Automatic reset of recorded locked layers when the Maya scene is reset.
struct SceneResetListener : public PXR_NS::TfWeakBase
{
SceneResetListener()
{
PXR_NS::TfWeakPtr<SceneResetListener> me(this);
PXR_NS::TfNotice::Register(me, &SceneResetListener::OnSceneReset);
}

void OnSceneReset(const UsdMayaSceneResetNotice&)
{
// Make sure we don't hold onto locked layers now that the
// Maya scene is reset.
forgetSystemLockedLayers();
}
};

// The set of locked layers.
//
// Kept in a function to avoid problem with the order of construction
// of global variables in C++.
using LockedLayers = std::set<PXR_NS::SdfLayerRefPtr>;
LockedLayers& getLockedLayers()
{
// Note: C++ guarantees correct multi-thread protection for static
// variables initialization in functions.
static SceneResetListener onSceneResetListener;
static LockedLayers layers;
return layers;
}
} // namespace

void addSystemLockedLayer(const PXR_NS::SdfLayerRefPtr& layer)
{
if (!layer)
return;

LockedLayers& layers = getLockedLayers();
auto iter = layers.find(layer);
if (iter != layers.end()) {
return;
}
layers.insert(layer);
}

void removeSystemLockedLayer(const PXR_NS::SdfLayerRefPtr& layer)
{
if (!layer)
return;

LockedLayers& layers = getLockedLayers();
layers.erase(layer);
}

bool isLayerSystemLocked(const PXR_NS::SdfLayerRefPtr& layer)
{
if (!layer)
return false;

LockedLayers& layers = getLockedLayers();
auto iter = layers.find(layer);
if (iter != layers.end()) {
return true;
}
return false;
}

void forgetSystemLockedLayers()
{
LockedLayers& layers = getLockedLayers();
layers.clear();
}

} // namespace MAYAUSD_NS_DEF
89 changes: 89 additions & 0 deletions lib/mayaUsd/utils/layerLocking.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// 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 MAYAUSD_LAYERLOCKING_H
#define MAYAUSD_LAYERLOCKING_H

#include <mayaUsd/base/api.h>
#include <mayaUsd/nodes/proxyShapeBase.h>

#include <pxr/usd/sdf/layer.h>
#include <pxr/usd/usd/stage.h>

namespace MAYAUSD_NS_DEF {

// The lock state of a 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 locked layers, a single
// layer could be locked in one stage and not locked in another stage. So, the
// locked state 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 layer locked state.
//
// Additionally, there are multiple levels of locking defined for a layer
// 1- A layer is locked if layer's permission to edit is set to false
// 2- A layer is system locked by setting a layer's permission to edit
// and a layer's permission to save both to false.
//
// However, the USD API for checking permissions is a result of the following
// conditions:
//
// For permission to save to be True (SdfLayer::PermissionToSave()):
//
// 1- The layer must not be anonymous
// 2- The layer must not be muted
// 3- The layer must have write access to disk
// 4- The internal _permissionToSave must be True
//
// For permission to Edit (SdfLayer::PermissionToEdit()):
//
// 1- The layer must not be muted
// 2- The internal _permissionToEdit must be True
//
// For this reason, the locked layer state needs to be managed inside Maya USD
// to avoid receiving false positives for PermissionToSave and PermissionToEdit.
//
// We therefore save the lock state of layers.
// OpenUSD forgets everything about layer permissions to save or edit as there are only applicable
// to sessions
//
// So we need to hold on to locked layers. We do this in a private global list
// of locked layers. That list gets cleared when a new Maya scene is created.

/*! \brief Adds a layer to the system lock list
*/
MAYAUSD_CORE_PUBLIC
void addSystemLockedLayer(const PXR_NS::SdfLayerRefPtr& layer);

/*! \brief Removes a layer from the system lock list
*/
MAYAUSD_CORE_PUBLIC
void removeSystemLockedLayer(const PXR_NS::SdfLayerRefPtr& layer);

/*! \brief Checks if a layer is in the lock list.
*/
MAYAUSD_CORE_PUBLIC
bool isLayerSystemLocked(const PXR_NS::SdfLayerRefPtr& layer);

/*! \brief Clears the lock list
*/
MAYAUSD_CORE_PUBLIC
void forgetSystemLockedLayers();

} // namespace MAYAUSD_NS_DEF

#endif
2 changes: 1 addition & 1 deletion lib/usd/ui/layerEditor/abstractCommandHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class AbstractCommandHook
virtual void muteSubLayer(UsdLayer usdLayer, bool muteIt) = 0;

// lock or unlock the given layer
virtual void lockSubLayer(UsdLayer usdLayer, bool lockIt) = 0;
virtual void lockSubLayer(UsdLayer usdLayer, bool lockIt, bool systemLock) = 0;

// starts a complex undo operation in the host app. Please use UndoContext class to safely
// open/close
Expand Down
Loading

0 comments on commit 00346db

Please sign in to comment.