Skip to content

Commit

Permalink
Merge pull request #3953 from Autodesk/bailp/EMSUSD-1222/save-up-axis…
Browse files Browse the repository at this point in the history
…-units

EMSUSD-1222 set up-axis and units on saved layers
  • Loading branch information
seando-adsk authored Oct 10, 2024
2 parents bb43afa + ac3fc56 commit b8b343c
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/mayaUsd/nodes/layerManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,8 @@ MStatus addLayerToBuilder(
auto fileFormatIdToken = layer->GetFileFormat()->GetFormatId();
fileFormatIdHandle.setString(UsdMayaUtil::convert(fileFormatIdToken.GetString()));

MayaUsd::utils::setLayerUpAxisAndUnits(layer);

std::string temp;
if (!stubOnly && ((exportOnlyIfDirty && layer->IsDirty()) || !exportOnlyIfDirty)) {
if (!layer->ExportToString(&temp)) {
Expand Down
20 changes: 20 additions & 0 deletions lib/mayaUsd/utils/utilSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <pxr/usd/usd/usdFileFormat.h>
#include <pxr/usd/usd/usdaFileFormat.h>
#include <pxr/usd/usd/usdcFileFormat.h>
#include <pxr/usd/usdGeom/tokens.h>

#include <maya/MGlobal.h>
#include <maya/MString.h>
Expand Down Expand Up @@ -359,6 +360,24 @@ static bool isCompatibleWithSave(
}
}

void setLayerUpAxisAndUnits(const SdfLayerRefPtr& layer)
{
if (!layer)
return;

const PXR_NS::TfToken upAxis
= MGlobal::isZAxisUp() ? PXR_NS::UsdGeomTokens->z : PXR_NS::UsdGeomTokens->y;
const double metersPerUnit
= UsdMayaUtil::ConvertMDistanceUnitToUsdGeomLinearUnit(MDistance::internalUnit());

// Note: code similar to what UsdGeomSetStageUpAxis -> UsdStage::SetMetadata end-up doing,
// but without having to have a stage. We basically set metadat on the virtual root object
// of the layer.
layer->SetField(
PXR_NS::SdfPath::AbsoluteRootPath(), PXR_NS::UsdGeomTokens->metersPerUnit, metersPerUnit);
layer->SetField(PXR_NS::SdfPath::AbsoluteRootPath(), PXR_NS::UsdGeomTokens->upAxis, upAxis);
}

bool saveLayerWithFormat(
SdfLayerRefPtr layer,
const std::string& requestedFilePath,
Expand All @@ -371,6 +390,7 @@ bool saveLayerWithFormat(
= requestedFormatArg.empty() ? usdFormatArgOption() : requestedFormatArg;

UsdMayaUtilFileSystem::updatePostponedRelativePaths(layer, filePath);
setLayerUpAxisAndUnits(layer);

if (isCompatibleWithSave(layer, filePath, formatArg)) {
if (!layer->Save()) {
Expand Down
5 changes: 5 additions & 0 deletions lib/mayaUsd/utils/utilSerialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ PXR_NS::SdfLayerRefPtr saveAnonymousLayer(
std::string formatArg = "",
std::string* errorMsg = nullptr);

/*! \brief Set the up-axis and units metadata according to the current Maya preferences.
*/
MAYAUSD_CORE_PUBLIC
void setLayerUpAxisAndUnits(const SdfLayerRefPtr& layer);

/*! \brief Update the list of sub-layers with a new layer identity.
* The new sub-layer is identified by its path explicitly,
* because a given layer might get referenced through multiple
Expand Down
1 change: 1 addition & 0 deletions test/lib/mayaUsd/fileio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(TEST_SCRIPT_FILES
testDisplayLayerSaveRestore.py
testSaveMutedAnonLayer.py
testSaveLockedAnonLayer.py
testSaveUpAxisAndUnits.py
testNonLocalEditTargetLayer.py

# Once of the tests in this file requires UsdMaya (from the Pixar plugin). That test
Expand Down
123 changes: 123 additions & 0 deletions test/lib/mayaUsd/fileio/testSaveUpAxisAndUnits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/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 cmds
from maya import standalone

import mayaUtils
import mayaUsd
import mayaUsd_createStageWithNewLayer

import usdUtils

from pxr import Usd, Sdf, UsdGeom

import unittest

class SaveUpAxisAndUnitsTest(unittest.TestCase):
'''
Test saving a layer and reloading the scene to verify they have up-axis and units metadata.
'''

pluginsLoaded = False

@classmethod
def setUpClass(cls):
fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False)

if not cls.pluginsLoaded:
cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

@classmethod
def tearDownClass(cls):
standalone.uninitialize()

def setUp(self):
cmds.file(new=True, force=True)

def testSaveLayerInUSD(self):
# Save the file. Make sure the USD edits will go to a USD file.
self._runTestSaveLayer(1)

def testSaveLayerInMaya(self):
# Save the file. Make sure the USD edits will go to the Maya file.
self._runTestSaveLayer(2)

def _runTestSaveLayer(self, saveLocation):
'''
The goal is to create an anonymous sub-layer, verify the up-axis and units
metadata is present when reloaded.
'''
# Create a stage with a sub-layer.
psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
rootLayer = stage.GetRootLayer()
subLayer = usdUtils.addNewLayerToLayer(rootLayer, anonymous=True)

# Add a prim in the root and in the sub-layer.
with Usd.EditContext(stage, Usd.EditTarget(rootLayer)):
stage.DefinePrim('/InRoot', 'Sphere')

with Usd.EditContext(stage, Usd.EditTarget(subLayer)):
stage.DefinePrim('/InSub', 'Cube')

def verifyPrims(stage):
inRoot = stage.GetPrimAtPath("/InRoot")
self.assertTrue(inRoot)
inRoot = None
inSub = stage.GetPrimAtPath("/InSub")
self.assertTrue(inSub)
inSub = None

verifyPrims(stage)

# Save the file. Make sure the edit will go where requested by saveLocation.
tempMayaFile = 'saveLockedAnonLayer.ma'
cmds.optionVar(intValue=('mayaUsd_SerializedUsdEditsLocation', saveLocation))
cmds.file(rename=tempMayaFile)
cmds.file(save=True, force=True, type='mayaAscii')

# Clear and reopen the file.
stage = None
rootLayer = None
subLayer = None
cmds.file(new=True, force=True)
cmds.file(tempMayaFile, open=True)

# Retrieve the stage, root and sub-layer.
stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
rootLayer = stage.GetRootLayer()
self.assertEqual(len(rootLayer.subLayerPaths), 1)
subLayerPath = rootLayer.subLayerPaths[0]
self.assertIsNotNone(subLayerPath)
self.assertTrue(subLayerPath)
subLayer = Sdf.Layer.FindOrOpen(subLayerPath)
self.assertIsNotNone(subLayer)

self.assertTrue(stage.HasAuthoredMetadata(UsdGeom.Tokens.metersPerUnit))
self.assertEqual(UsdGeom.GetStageMetersPerUnit(stage), 0.01)
self.assertTrue(stage.HasAuthoredMetadata(UsdGeom.Tokens.upAxis))
self.assertEqual(UsdGeom.GetStageUpAxis(stage), UsdGeom.Tokens.y)

# Verify the two objects are still present.
verifyPrims(stage)

if __name__ == '__main__':
unittest.main(verbosity=2)

0 comments on commit b8b343c

Please sign in to comment.