Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made all dirty layers and anonymous sublayers serialisable #1872

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
{
}