Skip to content
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
39 changes: 28 additions & 11 deletions lib/mayaUsd/commands/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,27 @@ pipeline-specific operations and/or early prototyping of features that might
otherwise not make sense to be part of the mainline codebase.

Chasers are registered with a particular name and can be passed argument
name/value pairs in an invocation of `mayaUSDImport`. There is no "plugin
discovery" method here – the developer/user is responsible for making sure the
chaser is registered via a call to the convenience macro
`USDMAYA_DEFINE_IMPORT_CHASER_FACTORY(name, ctx)`, where `name` is the name of
the chaser being created. Unlike export chasers, import chasers also have the
ability to define `Undo` and `Redo` methods in order to allow the
name/value pairs in an invocation of `mayaUSDImport`. The chaser is registered
via a call to the convenience macro `USDMAYA_DEFINE_IMPORT_CHASER_FACTORY(name,
ctx)`, where `name` is the name of the chaser being created. Import chaser
plugins can be discovered and loaded automatically by declaring the
`UsdMaya:ImportChaserPlugin` metadata in their `plugInfo.json`:

```json
"Info": {
"UsdMaya": {
"ImportChaserPlugin": {}
}
}
```

Unlike export chasers, import chasers also have the ability to define `Undo` and
`Redo` methods in order to allow the
`mayaUSDImport` command to remain compliant with the Maya undo stack. It's not
necessary to compile your chaser plugin together with `mayaUsdPlugin` in order
to work; you can create a completely separate maya DLL that contains the
business logic of your chaser code, and just call the aforementioned
`USDMAYA_DEFINE_IMPORT_CHASER_FACTORY` to register it, as long as the
`mayaUsdPlugin` DLL is loaded first.
`USDMAYA_DEFINE_IMPORT_CHASER_FACTORY` to register it.

A sample import chaser, `infoImportChaser.cpp`, is provided to give an example
of how to write an import chaser. All it does is read any custom layer data in
Expand Down Expand Up @@ -399,9 +408,17 @@ implement prim post-processing that executes immediately after prims
are written (and/or after animation is written to a prim in time-based
exports). Chasers are registered with a particular name and can be
passed argument name/value pairs in an invocation of a concrete
`MayaUSDExportCommand` command. There is no "plugin discovery" method
here – the developer/user is responsible for making sure the chaser is
registered.
`MayaUSDExportCommand` command. Export chaser plugins can be discovered
and loaded automatically by declaring the `UsdMaya:ExportChaserPlugin`
metadata in their `plugInfo.json`:

```json
"Info": {
"UsdMaya": {
"ExportChaserPlugin": {}
}
}
```

For example the pxr plug-in provides one such chaser plugin called
`AlembicChaser` to try to make integrating USD into Alembic-heavy
Expand Down
2 changes: 2 additions & 0 deletions lib/mayaUsd/fileio/chaser/exportChaserRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ bool UsdMayaExportChaserRegistry::RegisterFactory(
UsdMayaExportChaserRefPtr
UsdMayaExportChaserRegistry::Create(const std::string& name, const FactoryContext& context) const
{
UsdMaya_RegistryHelper::LoadExportChaserPlugins();
TfRegistryManager::GetInstance().SubscribeTo<UsdMayaExportChaserRegistry>();

if (UsdMayaExportChaserRegistry::FactoryFn fn = _factoryRegistry[name]) {
return TfCreateRefPtr(fn(context));
} else {
Expand Down
2 changes: 2 additions & 0 deletions lib/mayaUsd/fileio/chaser/importChaserRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ bool UsdMayaImportChaserRegistry::RegisterFactory(const char* name, FactoryFn fn
UsdMayaImportChaserRefPtr
UsdMayaImportChaserRegistry::Create(const char* name, const FactoryContext& context) const
{
UsdMaya_RegistryHelper::LoadImportChaserPlugins();
TfRegistryManager::GetInstance().SubscribeTo<UsdMayaImportChaserRegistry>();

if (UsdMayaImportChaserRegistry::FactoryFn fn = _factoryImportRegistry[name]) {
return TfCreateRefPtr(fn(context));
} else {
Expand Down
126 changes: 56 additions & 70 deletions lib/mayaUsd/fileio/registryHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ TF_DEFINE_PRIVATE_TOKENS(
(UsdMaya)
(ShadingModePlugin)
(JobContextPlugin)
(ImportChaserPlugin)
(ExportChaserPlugin)
);
// clang-format on

Expand Down Expand Up @@ -140,6 +142,42 @@ static bool _HasMayaPlugin(
return true;
}

static void _FindAndLoadUsdMayaPlugins(const TfToken& pluginKey)
{
const std::vector<TfToken> scope { _tokens->UsdMaya, pluginKey };

for (const auto& plug : PlugRegistry::GetInstance().GetAllPlugins()) {
std::string mayaPlugin;
if (_HasMayaPlugin(plug, scope, &mayaPlugin)) {
if (!mayaPlugin.empty()) {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"Found %s %s: Loading via Maya API %s.\n",
pluginKey.GetText(),
plug->GetName().c_str(),
mayaPlugin.c_str());
std::string loadPluginCmd
= TfStringPrintf("loadPlugin -quiet %s", mayaPlugin.c_str());
if (MGlobal::executeCommand(loadPluginCmd.c_str())) {
// Need to ensure Python script modules are loaded
// properly for this library (Maya's loadPlugin will not
// load script modules like TfDlopen would).
TfScriptModuleLoader::GetInstance().LoadModules();
} else {
TF_CODING_ERROR("Unable to load mayaplugin %s\n", mayaPlugin.c_str());
}
} else {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"Found %s %s: Loading via USD API.\n",
pluginKey.GetText(),
plug->GetName().c_str());
plug->Load();
}
}
}
}

/* static */
std::string _PluginDictScopeToDebugString(const std::vector<TfToken>& scope)
{
Expand Down Expand Up @@ -202,81 +240,29 @@ void UsdMaya_RegistryHelper::FindAndLoadMayaPlug(
/* static */
void UsdMaya_RegistryHelper::LoadShadingModePlugins()
{
static std::once_flag _shadingModesLoaded;
static std::vector<TfToken> scope = { _tokens->UsdMaya, _tokens->ShadingModePlugin };
std::call_once(_shadingModesLoaded, []() {
PlugPluginPtrVector plugins = PlugRegistry::GetInstance().GetAllPlugins();
std::string mayaPlugin;
TF_FOR_ALL(plugIter, plugins)
{
PlugPluginPtr plug = *plugIter;
if (_HasMayaPlugin(plug, scope, &mayaPlugin)) {
if (!mayaPlugin.empty()) {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"Found shading mode plugin %s: Loading via Maya API %s.\n",
plug->GetName().c_str(),
mayaPlugin.c_str());
std::string loadPluginCmd
= TfStringPrintf("loadPlugin -quiet %s", mayaPlugin.c_str());
if (MGlobal::executeCommand(loadPluginCmd.c_str())) {
// Need to ensure Python script modules are loaded
// properly for this library (Maya's loadPlugin will not
// load script modules like TfDlopen would).
TfScriptModuleLoader::GetInstance().LoadModules();
} else {
TF_CODING_ERROR("Unable to load mayaplugin %s\n", mayaPlugin.c_str());
}
} else {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"Found shading mode plugin %s: Loading via USD API.\n",
plug->GetName().c_str());
plug->Load();
}
}
}
});
static std::once_flag _loaded;
std::call_once(_loaded, []() { _FindAndLoadUsdMayaPlugins(_tokens->ShadingModePlugin); });
}

/* static */
void UsdMaya_RegistryHelper::LoadJobContextPlugins()
{
static std::once_flag _jobContextsLoaded;
static std::vector<TfToken> scope = { _tokens->UsdMaya, _tokens->JobContextPlugin };
std::call_once(_jobContextsLoaded, []() {
PlugPluginPtrVector plugins = PlugRegistry::GetInstance().GetAllPlugins();
std::string mayaPlugin;
TF_FOR_ALL(plugIter, plugins)
{
PlugPluginPtr plug = *plugIter;
if (_HasMayaPlugin(plug, scope, &mayaPlugin)) {
if (!mayaPlugin.empty()) {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"Found job context plugin %s: Loading via Maya API %s.\n",
plug->GetName().c_str(),
mayaPlugin.c_str());
std::string loadPluginCmd
= TfStringPrintf("loadPlugin -quiet %s", mayaPlugin.c_str());
if (MGlobal::executeCommand(loadPluginCmd.c_str())) {
// Need to ensure Python script modules are loaded
// properly for this library (Maya's loadPlugin will not
// load script modules like TfDlopen would).
TfScriptModuleLoader::GetInstance().LoadModules();
} else {
TF_CODING_ERROR("Unable to load mayaplugin %s\n", mayaPlugin.c_str());
}
} else {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg(
"Found job context plugin %s: Loading via USD API.\n",
plug->GetName().c_str());
plug->Load();
}
}
}
});
static std::once_flag _loaded;
std::call_once(_loaded, []() { _FindAndLoadUsdMayaPlugins(_tokens->JobContextPlugin); });
}

/* static */
void UsdMaya_RegistryHelper::LoadImportChaserPlugins()
{
static std::once_flag _loaded;
std::call_once(_loaded, []() { _FindAndLoadUsdMayaPlugins(_tokens->ImportChaserPlugin); });
}

/* static */
void UsdMaya_RegistryHelper::LoadExportChaserPlugins()
{
static std::once_flag _loaded;
std::call_once(_loaded, []() { _FindAndLoadUsdMayaPlugins(_tokens->ExportChaserPlugin); });
}

/* static */
Expand Down
24 changes: 24 additions & 0 deletions lib/mayaUsd/fileio/registryHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,30 @@ struct UsdMaya_RegistryHelper
/// usdMaya will try to load the "mayaPlugin" when job contexts are first accessed.
static void LoadJobContextPlugins();

/// Searches the plugInfos and looks for ImportChaserPlugin.
///
/// "UsdMaya" : {
/// "ImportChaserPlugin" : {
/// "mayaPlugin" : "myImportChaserPlugin"
/// }
/// }
///
/// At that scope, it expects an optional "mayaPlugin" key.
/// usdMaya will try to load the plugin when import chasers are first accessed.
static void LoadImportChaserPlugins();

/// Searches the plugInfos and looks for ExportChaserPlugin.
///
/// "UsdMaya" : {
/// "ExportChaserPlugin" : {
/// "mayaPlugin" : "myExportChaserPlugin"
/// }
/// }
///
/// At that scope, it expects an optional "mayaPlugin" key.
/// usdMaya will try to load the plugin when export chasers are first accessed.
static void LoadExportChaserPlugins();

/// Searches the plugInfos for metadata dictionaries at the given \p scope,
/// and composes them together.
/// The scope are the nested keys to search through in the plugInfo (for
Expand Down
6 changes: 6 additions & 0 deletions test/lib/usd/plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ if(IS_MACOSX OR IS_LINUX)
mayaUsd_install_rpath(rpath ${TARGET_NAME})
endif()

# -----------------------------------------------------------------------------
# Plug Plug-in file
# -----------------------------------------------------------------------------
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/infoImportChaser_plugInfo.json"
"${CMAKE_CURRENT_BINARY_DIR}/infoImportChaser/plugInfo.json"
)

# -----------------------------------------------------------------------------
# Plug Plug-ins
Expand Down
16 changes: 16 additions & 0 deletions test/lib/usd/plugin/infoImportChaser_plugInfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"Plugins": [
{
"Info": {
"UsdMaya": {
"ImportChaserPlugin": {
"mayaPlugin": "usdTestInfoImportChaser"
}
}
},
"Name": "usdTestInfoImportChaser",
"LibraryPath": "@CMAKE_CURRENT_BINARY_DIR@/@CMAKE_SHARED_LIBRARY_PREFIX@usdTestInfoImportChaser@CMAKE_SHARED_LIBRARY_SUFFIX@",
"Type": "library"
}
]
}
1 change: 1 addition & 0 deletions test/lib/usd/plugin/nullApiExporter_plugInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"Info": {
"UsdMaya": {
"JobContextPlugin": {},
"ExportChaserPlugin": {},
"SchemaApiAdaptor": {
"providesTranslator": [
"shape"
Expand Down
2 changes: 1 addition & 1 deletion test/lib/usd/translators/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ mayaUsd_add_test(testUsdImportChaser
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
ENV
"MAYA_PLUG_IN_PATH=${CMAKE_CURRENT_BINARY_DIR}/../plugin"
"${PXR_OVERRIDE_PLUGINPATH_NAME}=${CMAKE_CURRENT_BINARY_DIR}/../plugin"
"${PXR_OVERRIDE_PLUGINPATH_NAME}=${CMAKE_CURRENT_BINARY_DIR}/../plugin/infoImportChaser"
"LD_LIBRARY_PATH=${ADDITIONAL_LD_LIBRARY_PATH}"
)
set_property(TEST testUsdImportChaser APPEND PROPERTY LABELS translators)
Expand Down
20 changes: 19 additions & 1 deletion test/lib/usd/translators/testUsdExportSchemaApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import maya.api.OpenMaya as om
import maya.api.OpenMayaAnim as oma

from pxr import Tf, Gf, Usd
from pxr import Plug, Tf, Gf, Usd

import fixturesUtils

Expand Down Expand Up @@ -137,6 +137,24 @@ def testExportJobContextConflicts(self):

cmds.file(f=True, new=True)

def testExportChaserPluginLoading(self):
"""Testing that export chaser plugins can be automatically loaded by mayaUSDExport from plugInfo."""

chaserPlugin = Plug.Registry().GetPluginWithName("usdTestApiWriter")

# Ensure the plugin is not loaded before the test
self.assertIsNotNone(chaserPlugin)
self.assertFalse(chaserPlugin.isLoaded)

cmds.polySphere(r=1)
usdFilePath = os.path.abspath('UsdExportChaserPluginLoading.usda')
cmds.mayaUSDExport(file=usdFilePath, chaser=["NullAPIChaser"])

# Verify the plugin was loaded by the mayaUSDExport call.
self.assertTrue(chaserPlugin.isLoaded)

cmds.file(f=True, new=True)

@unittest.skipUnless(HAS_BULLET and HAS_USDMAYA, 'Requires the Pixar and bullet plugins.')
def testPluginSchemaAdaptors(self):
"""Testing a plugin Schema adaptor in a generic context:"""
Expand Down
8 changes: 7 additions & 1 deletion test/lib/usd/translators/testUsdImportChaser.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,14 @@ def tearDownClass(cls):
mayastandalone.uninitialize()

def testImportChaser(self):
cmds.loadPlugin('usdTestInfoImportChaser') # NOTE: (yliangsiew) We load this plugin since the chaser is compiled as part of it.
# ensure the plugin is not loaded, e.g. by another test
self.assertFalse(cmds.pluginInfo('usdTestInfoImportChaser', q=True, loaded=True))

rootPaths = cmds.mayaUSDImport(v=True, f=self.stagePath, chaser=['info'])

# verify that the plugin was discovered and loaded
self.assertTrue(cmds.pluginInfo('usdTestInfoImportChaser', q=True, loaded=True))

self.assertEqual(len(rootPaths), 1)
sl = om.MSelectionList()
sl.add(rootPaths[0])
Expand Down
Loading