Skip to content

Commit

Permalink
Merge pull request #1872 from AnimalLogic/J-Mo63/serialisable-anonymo…
Browse files Browse the repository at this point in the history
…us-layers

Made all dirty layers and anonymous sublayers serialisable
  • Loading branch information
Krystian Ligenza authored Dec 7, 2021
2 parents 47ad5db + 8bc36df commit ac657cb
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 14 deletions.
58 changes: 45 additions & 13 deletions plugin/al/lib/AL_USDMaya/AL/usdmaya/nodes/LayerManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <mayaUsd/listeners/notice.h>

#include <pxr/base/tf/stringUtils.h>
#include <pxr/usd/sdf/textFileFormat.h>
#include <pxr/usd/usd/usdFileFormat.h>
#include <pxr/usd/usd/usdaFileFormat.h>
Expand Down Expand Up @@ -519,19 +520,21 @@ void LayerManager::loadAllLayers()
_layerManagerProfilerCategory, MProfiler::kColorE_L3, "Load all layers");

TF_DEBUG(ALUSDMAYA_LAYERS).Msg("LayerManager::loadAllLayers\n");
const char* errorString = "LayerManager::loadAllLayers";
const char* identifierTempSuffix = "_tmp";
MStatus status;
MPlug allLayersPlug = layersPlug();
MPlug singleLayerPlug;
MPlug idPlug;
MPlug fileFormatIdPlug;
MPlug anonymousPlug;
MPlug serializedPlug;
std::string identifierVal;
std::string fileFormatIdVal;
std::string serializedVal;
SdfLayerRefPtr layer;
const char* errorString = "LayerManager::loadAllLayers";
const char* identifierTempSuffix = "_tmp";
MStatus status;
MPlug allLayersPlug = layersPlug();
MPlug singleLayerPlug;
MPlug idPlug;
MPlug fileFormatIdPlug;
MPlug anonymousPlug;
MPlug serializedPlug;
std::string identifierVal;
std::string fileFormatIdVal;
std::string serializedVal;
std::string sessionLayerIdentifier;
std::map<std::string, std::string> sessionSublayerNames;
SdfLayerRefPtr layer;
// We DON'T want to use evaluate num elements, because we don't want to trigger
// a compute - we want the value(s) as read from the file!
const unsigned int numElements = allLayersPlug.numElements();
Expand Down Expand Up @@ -575,8 +578,20 @@ void LayerManager::loadAllLayers()
if (isAnon) {
// Note that the new identifier will not match the old identifier - only the "tag" will
// be retained
// if this layer is an anonymous sublayer of the session layer, these will be replaced
// with the new identifier
layer
= SdfLayer::CreateAnonymous(SdfLayer::GetDisplayNameFromIdentifier(identifierVal));

// store old:new name so we can replace the session layers anonymous subLayers
sessionSublayerNames[identifierVal] = layer->GetIdentifier().c_str();

// Check if this is the session layer
// Used later to update the session layers anonymous subLayer naming
if (sessionLayerIdentifier.empty() && TfStringEndsWith(identifierVal, "session.usda")) {
sessionLayerIdentifier = layer->GetIdentifier().c_str();
}

} else {
SdfLayerHandle layerHandle = SdfLayer::Find(identifierVal);
if (layerHandle) {
Expand Down Expand Up @@ -641,6 +656,23 @@ void LayerManager::loadAllLayers()
"################################################\n");
addLayer(layer, identifierVal);
}

// Update the name of any anonymous sublayers in the session layer
SdfLayerHandle sessionLayer = SdfLayer::Find(sessionLayerIdentifier);

if (sessionLayer) {
// TODO drill down and apply through session sublayers to enable recursive anonymous
// sublayers
auto subLayerPaths = sessionLayer->GetSubLayerPaths();

typedef std::map<std::string, std::string>::const_iterator MapIterator;
for (MapIterator iter = sessionSublayerNames.begin(); iter != sessionSublayerNames.end();
iter++) {
std::replace(subLayerPaths.begin(), subLayerPaths.end(), iter->first, iter->second);
}

sessionLayer->SetSubLayerPaths(subLayerPaths);
}
}

//----------------------------------------------------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions plugin/al/lib/AL_USDMaya/AL/usdmaya/nodes/ProxyShape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,8 @@ void ProxyShape::serialize(UsdStageRefPtr stage, LayerManager* layerManager)

// Then add in the current edit target
trackEditTargetLayer(layerManager);
// Add all dirty layers
trackAllDirtyLayers(layerManager);
} else {
MGlobal::displayError(
"ProxyShape::serialize was passed a nullptr for the layerManager");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,85 @@ TEST(ProxyShape, editTargetChangeAndSave)
}
}

// Test round trip of dirty layers serialization and deserialization
TEST(ProxyShape, roundtripDirtyLayers)
{
MFileIO::newFile(true);
std::string filePath = std::string(AL_USDMAYA_TEST_DATA) + "/multiple_layers.usda";
const MString savedFile = buildTempPath("AL_USDMayaTests_roundtripDirtyLayers.ma");

{
UsdStageRefPtr stage = UsdStage::Open(filePath);

UsdStageCache::Id stageCacheId = AL::usdmaya::StageCache::Get().Insert(stage);
EXPECT_TRUE(stageCacheId.IsValid());

SdfLayerRefPtr layer1;
SdfLayerRefPtr layer2;
for (auto& layer : stage->GetUsedLayers()) {
if (layer->GetIdentifier().find("layer1") != std::string::npos) {
layer1 = layer;
} else if (layer->GetIdentifier().find("layer2") != std::string::npos) {
layer2 = layer;
}
}
ASSERT_TRUE(layer1);
ASSERT_TRUE(layer2);

// Modify layer1 before creating proxy
layer1->SetCustomLayerData(
{ std::make_pair("layer1_key", VtValue(std::string("layer1_value"))) });
ASSERT_TRUE(layer1->IsDirty());

// Load the stage into Maya
MString importCommand = MString("AL_usdmaya_ProxyShapeImport ") + "-name \"testProxy\" "
+ "-stageId " + stageCacheId.ToLongInt();
MStringArray cmdResults;
MGlobal::executeCommand(importCommand, cmdResults, true);

// Modify layer2 after creating the proxy
layer2->SetCustomLayerData(
{ std::make_pair("layer2_key", VtValue(std::string("layer2_value"))) });
ASSERT_TRUE(layer2->IsDirty());

EXPECT_EQ(MStatus(MS::kSuccess), MFileIO::saveAs(savedFile, NULL, true));
}

// Reopen maya scene
MFileIO::newFile(true);
EXPECT_EQ(MStatus(MS::kSuccess), MFileIO::open(savedFile, NULL, true));

auto stages = AL::usdmaya::StageCache::Get().GetAllStages();
EXPECT_TRUE(!stages.empty());

auto stage = stages[0];
SdfLayerRefPtr layer1;
SdfLayerRefPtr layer2;
for (auto& layer : stage->GetUsedLayers()) {
if (layer->GetIdentifier().find("layer1") != std::string::npos) {
layer1 = layer;
} else if (layer->GetIdentifier().find("layer2") != std::string::npos) {
layer2 = layer;
}
}
ASSERT_TRUE(layer1);
ASSERT_TRUE(layer2);

auto layer1CustomData(layer1->GetCustomLayerData());
EXPECT_EQ(layer1CustomData.size(), unsigned(1));
EXPECT_EQ(layer1CustomData.count("layer1_key"), unsigned(1));
EXPECT_EQ(layer1CustomData["layer1_key"], VtValue(std::string("layer1_value")));

auto layer2CustomData(layer2->GetCustomLayerData());
EXPECT_EQ(layer2CustomData.size(), unsigned(1));
EXPECT_EQ(layer2CustomData.count("layer2_key"), unsigned(1));
EXPECT_EQ(layer2CustomData["layer2_key"], VtValue(std::string("layer2_value")));

// Clear out the scene to avoid crashing in proxy shape code during idle
// redraw.
MFileIO::newFile(true);
}

// Test translating a Mesh Prim via the command
TEST(ManualTranslate, importMeshPrim)
{
Expand Down
66 changes: 65 additions & 1 deletion plugin/al/plugin/AL_USDMayaTestPlugin/py/testLayerManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from maya import cmds
from AL.usdmaya import ProxyShape

from pxr import Sdf, Usd
from pxr import Sdf, Usd, UsdUtils

class TestLayerManagerSerialisation(unittest.TestCase):
"""Test cases for layer manager serialisation and deserialisation"""
Expand Down Expand Up @@ -207,6 +207,70 @@ def _reloadAndAssert(usdFilePath, proxyName):
proxyName = _buildAndEditAndSaveScene(self._usdFilePath)
_reloadAndAssert(self._usdFilePath, proxyName)

def test_anonymousLayerSerialisation(self):
"""Tests multiple anonymous sublayers can be saved and restored.
"""

def _setupStage():
# Create a prim at root layer
rootLayer = self._stage.GetRootLayer()
Sdf.CreatePrimInLayer(rootLayer, "/root")

# Create session layer with prim
sessionLayer = self._stage.GetSessionLayer()
Sdf.CreatePrimInLayer(sessionLayer, "/root/prim01")

# Create anonymous sublayers
sublayer01 = Sdf.Layer.CreateAnonymous()
Sdf.CreatePrimInLayer(sublayer01, "/root/subprim01")
sublayer02 = Sdf.Layer.CreateAnonymous()
Sdf.CreatePrimInLayer(sublayer02, "/root/subprim02")

# TODO easy way to add testing for recursive restoration
# sublayer03 = Sdf.Layer.CreateAnonymous()
# Sdf.CreatePrimInLayer(sublayer02, "/root/subprim02/subsubprim03")
# sublayer02.subLayerPaths = [sublayer03.identifier]

# Add sublayers to session layer
sessionLayer.subLayerPaths = [
sublayer01.identifier,
sublayer02.identifier
]

def _saveScene():
# Save Maya scene to temp file and close
cmds.file(rename=self._mayaFilePath)
cmds.file(save=True, force=True, type="mayaAscii")
return self._mayaFilePath

def _reloadScene(filename):
# Reopen the Maya scene file
cmds.file(new=True, force=True)
cmds.file(filename, open=True)
self._stage = ProxyShape.getByName('AL_usdmaya_Proxy').getUsdStage()

_setupStage()
file = _saveScene()
_reloadScene(file)

# Assert reloaded state of anonymous sublayers
sessionLayer = self._stage.GetSessionLayer()
self.assertEqual(len(sessionLayer.subLayerPaths), 2)

sublayer01 = Sdf.Find(sessionLayer.subLayerPaths[0])
sublayer02 = Sdf.Find(sessionLayer.subLayerPaths[1])

self.assertIsNotNone(sublayer01)
self.assertIsNotNone(sublayer02)
self.assertIsNotNone(sublayer01.GetPrimAtPath('/root/subprim01'))
self.assertIsNotNone(sublayer02.GetPrimAtPath('/root/subprim02'))

# TODO easy way to add testing for recursive restoration
# self.assertEqual(len(sublayer02.subLayerPaths), 1)
# sublayer03 = Sdf.Find(sublayer02.subLayerPaths[0])
# self.assertIsNone(sublayer03.GetPrimAtPath('/root/subprim02/subsubprim03'))


if __name__ == "__main__":

Expand Down
12 changes: 12 additions & 0 deletions plugin/al/plugin/AL_USDMayaTestPlugin/test_data/layer1.usda
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#usda 1.0
(
defaultPrim = "root"
)

def "root" ()
{
def "simple" (
)
{
}
}
12 changes: 12 additions & 0 deletions plugin/al/plugin/AL_USDMayaTestPlugin/test_data/layer2.usda
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#usda 1.0
(
defaultPrim = "root"
)

def "root" ()
{
def "other" (
)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#usda 1.0
(
defaultPrim = "root"
subLayers = [
@./layer1.usda@,
@./layer2.usda@,
]
)

def "root"
{
}

0 comments on commit ac657cb

Please sign in to comment.