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

Maya-128422 Override PrimReader #3164

Merged
merged 13 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion lib/mayaUsd/fileio/jobs/readJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ void UsdMaya_ReadJob::_DoImportPrimIt(

TfToken typeName = prim.GetTypeName();
if (UsdMayaPrimReaderRegistry::ReaderFactoryFn factoryFn
= UsdMayaPrimReaderRegistry::FindOrFallback(typeName)) {
= UsdMayaPrimReaderRegistry::FindOrFallback(typeName, mArgs, prim)) {
UsdMayaPrimReaderSharedPtr primReader = factoryFn(args);
if (primReader) {
TempNodeTrackerScope scope(readCtx);
Expand Down
9 changes: 9 additions & 0 deletions lib/mayaUsd/fileio/primReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ UsdMayaPrimReader::UsdMayaPrimReader(const UsdMayaPrimReaderArgs& args)
{
}

/* static */
UsdMayaPrimReader::ContextSupport
UsdMayaPrimReader::CanImport(const UsdMayaJobImportArgs&, const UsdPrim& importPrim)
{
// Default value for all readers is Fallback. More specialized writers can
// override the base CanImport to report Supported/Unsupported as necessary.
return ContextSupport::Fallback;
}

bool UsdMayaPrimReader::HasPostReadSubtree() const { return false; }

void UsdMayaPrimReader::PostReadSubtree(UsdMayaPrimReaderContext&) { }
Expand Down
19 changes: 19 additions & 0 deletions lib/mayaUsd/fileio/primReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ class UsdMayaPrimReader
UsdMayaPrimReader(const UsdMayaPrimReaderArgs&);
virtual ~UsdMayaPrimReader() {};

/// The level of support a reader can offer for a given context
///
/// A basic reader that gives correct results across most contexts should
/// report `Fallback`, while a specialized reader that really shines in a
/// given context should report `Supported` when the context is right and
/// `Unsupported` if the context is not as expected.
enum class ContextSupport
{
Supported,
Fallback,
Unsupported
};

/// This static function is expected for all prim readers and allows
/// declaring how well this class can support the current context:
MAYAUSD_CORE_PUBLIC
static ContextSupport
CanImport(const UsdMayaJobImportArgs& importArgs, const UsdPrim& importPrim);

/// Reads the USD prim given by the prim reader args into a Maya shape,
/// modifying the prim reader context as a result.
/// Returns true if successful.
Expand Down
133 changes: 116 additions & 17 deletions lib/mayaUsd/fileio/primReaderRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,42 @@ TF_DEFINE_PRIVATE_TOKENS(
);
// clang-format on

typedef std::map<TfToken, UsdMayaPrimReaderRegistry::ReaderFactoryFn> _Registry;
static _Registry _reg;
namespace {
struct _RegistryEntry
{
UsdMayaPrimReaderRegistry::ContextPredicateFn _pred;
UsdMayaPrimReaderRegistry::ReaderFactoryFn _fn;
int _index;
};

typedef std::unordered_multimap<TfToken, _RegistryEntry, TfToken::HashFunctor> _Registry;
static _Registry _reg;
static int _indexCounter = 0;

_Registry::const_iterator
_Find(const TfToken& typeName, const UsdMayaJobImportArgs& importArgs, const UsdPrim& importPrim)
{
using ContextSupport = UsdMayaPrimReader::ContextSupport;

_Registry::const_iterator ret = _reg.cend();
_Registry::const_iterator first, last;

std::tie(first, last) = _reg.equal_range(typeName);
while (first != last) {
ContextSupport support = first->second._pred(importArgs, importPrim);
// Look for a "Supported" reader. If no "Supported" reader is found, use a "Fallback" reader
if (support == ContextSupport::Supported) {
ret = first;
break;
} else if (support == ContextSupport::Fallback && ret == _reg.end()) {
ret = first;
}
++first;
}

return ret;
}
} // namespace

/* static */
void UsdMayaPrimReaderRegistry::Register(
Expand All @@ -53,13 +87,63 @@ void UsdMayaPrimReaderRegistry::Register(
bool fromPython)
{
TfToken tfTypeName(t.GetTypeName());
int index = _indexCounter++;
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg("Registering UsdMayaPrimReader for TfType %s.\n", tfTypeName.GetText());

// Use default ContextSupport if not specified
_reg.insert(std::make_pair(
tfTypeName,
_RegistryEntry { [](const UsdMayaJobImportArgs&, const UsdPrim) {
return UsdMayaPrimReader::ContextSupport::Fallback;
},
fn,
index }));

if (fn) {
UsdMaya_RegistryHelper::AddUnloader(
[tfTypeName, index]() {
_Registry::const_iterator it, itEnd;
std::tie(it, itEnd) = _reg.equal_range(tfTypeName);
for (; it != itEnd; ++it) {
if (it->second._index == index) {
_reg.erase(it);
break;
}
}
},
fromPython);
}
}

/* static */
void UsdMayaPrimReaderRegistry::Register(
const TfType& t,
UsdMayaPrimReaderRegistry::ContextPredicateFn pred,
UsdMayaPrimReaderRegistry::ReaderFactoryFn fn,
bool fromPython)
{
TfToken tfTypeName(t.GetTypeName());
int index = _indexCounter++;
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg("Registering UsdMayaPrimReader for TfType %s.\n", tfTypeName.GetText());
std::pair<_Registry::iterator, bool> insertStatus = _reg.insert(std::make_pair(tfTypeName, fn));
if (insertStatus.second) {
UsdMaya_RegistryHelper::AddUnloader([tfTypeName]() { _reg.erase(tfTypeName); }, fromPython);
} else {
TF_CODING_ERROR("Multiple readers for type %s", tfTypeName.GetText());
_reg.insert(std::make_pair(tfTypeName, _RegistryEntry { pred, fn, index }));

// The unloader uses the index to know which entry to erase when there are
// more than one for the same usdInfoId.
if (fn) {
UsdMaya_RegistryHelper::AddUnloader(
[tfTypeName, index]() {
_Registry::const_iterator it, itEnd;
std::tie(it, itEnd) = _reg.equal_range(tfTypeName);
for (; it != itEnd; ++it) {
if (it->second._index == index) {
_reg.erase(it);
break;
}
}
},
fromPython);
}
}

Expand All @@ -70,8 +154,10 @@ void UsdMayaPrimReaderRegistry::RegisterRaw(const TfType& t, UsdMayaPrimReaderRe
}

/* static */
UsdMayaPrimReaderRegistry::ReaderFactoryFn
UsdMayaPrimReaderRegistry::Find(const TfToken& usdTypeName)
UsdMayaPrimReaderRegistry::ReaderFactoryFn UsdMayaPrimReaderRegistry::Find(
const TfToken& usdTypeName,
const UsdMayaJobImportArgs& importArgs,
const UsdPrim& importPrim)
{
TfRegistryManager::GetInstance().SubscribeTo<UsdMayaPrimReaderRegistry>();

Expand All @@ -81,28 +167,41 @@ UsdMayaPrimReaderRegistry::Find(const TfToken& usdTypeName)
std::string typeNameStr = tfType.GetTypeName();
TfToken typeName(typeNameStr);
ReaderFactoryFn ret = nullptr;
if (TfMapLookup(_reg, typeName, &ret)) {
return ret;

// For that we are using unordered_multimap now, we can't use TfMapLookup anymore
_Registry::const_iterator it = _Find(typeName, importArgs, importPrim);

if (it != _reg.end()) {
return it->second._fn;
}

static const TfTokenVector SCOPE = { _tokens->UsdMaya, _tokens->PrimReader };
UsdMaya_RegistryHelper::FindAndLoadMayaPlug(SCOPE, typeNameStr);

// ideally something just registered itself. if not, we at least put it in
// the registry in case we encounter it again.
if (!TfMapLookup(_reg, typeName, &ret)) {
if (_reg.count(typeName) == 0) {
TF_DEBUG(PXRUSDMAYA_REGISTRY)
.Msg("No usdMaya reader plugin for TfType %s. No maya plugin.\n", typeName.GetText());
_reg[typeName] = nullptr;
//_reg[typeName] = nullptr;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might want to remove this commented line

Register(
tfType,
[](const UsdMayaJobImportArgs&, const UsdPrim&) {
return UsdMayaPrimReader::ContextSupport::Fallback;
},
nullptr);
}
return ret;

return nullptr;
}

/* static */
UsdMayaPrimReaderRegistry::ReaderFactoryFn
UsdMayaPrimReaderRegistry::FindOrFallback(const TfToken& usdTypeName)
UsdMayaPrimReaderRegistry::ReaderFactoryFn UsdMayaPrimReaderRegistry::FindOrFallback(
const TfToken& usdTypeName,
const UsdMayaJobImportArgs& importArgs,
const UsdPrim& importPrim)
{
if (ReaderFactoryFn fn = Find(usdTypeName)) {
if (ReaderFactoryFn fn = Find(usdTypeName, importArgs, importPrim)) {
return fn;
}

Expand Down
48 changes: 46 additions & 2 deletions lib/mayaUsd/fileio/primReaderRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ struct UsdMayaPrimReaderRegistry
/// for the given prim reader args.
typedef std::function<UsdMayaPrimReaderSharedPtr(const UsdMayaPrimReaderArgs&)> ReaderFactoryFn;

/// Predicate function, i.e. a function that can tell the level of support
/// the reader function will provide for a given context.
using ContextPredicateFn = std::function<
UsdMayaPrimReader::ContextSupport(const UsdMayaJobImportArgs&, const UsdPrim&)>;

/// Reader function, i.e. a function that reads a prim. This is the
/// signature of the function declared in the PXRUSDMAYA_DEFINE_READER
/// macro.
Expand All @@ -63,7 +68,16 @@ struct UsdMayaPrimReaderRegistry
MAYAUSD_CORE_PUBLIC
static void Register(const TfType& type, ReaderFactoryFn fn, bool fromPython = false);

/// \brief Register \p fn as a reader provider for \p type and provide the supportability.
MAYAUSD_CORE_PUBLIC
static void Register(
const TfType& type,
ContextPredicateFn pred,
ReaderFactoryFn fn,
bool fromPython = false);

/// \brief Register \p fn as a reader provider for \p T.
/// Context support will be set to default "Fallback"
///
/// Example for registering a reader factory in your custom plugin, assuming
/// that MyType is registered with the TfType system:
Expand All @@ -85,6 +99,30 @@ struct UsdMayaPrimReaderRegistry
}
}

/// \brief Register \p fn as a reader provider for \p T and provide the supportability.
/// Use "Supported" to override default reader
///
/// Example for registering a reader factory in your custom plugin, assuming
/// that MyType is registered with the TfType system:
/// \code{.cpp}
/// class MyReader : public UsdMayaPrimReader {
/// static UsdMayaPrimReaderSharedPtr Create(
/// const UsdMayaPrimReaderArgs&);
/// };
/// TF_REGISTRY_FUNCTION_WITH_TAG(UsdMayaPrimReaderRegistry, MyType) {
/// UsdMayaPrimReaderRegistry::Register<MyType>(ContextSupport, MyReader::Create);
/// }
/// \endcode
template <typename T>
static void Register(ContextPredicateFn pred, ReaderFactoryFn fn, bool fromPython = false)
{
if (TfType t = TfType::Find<T>()) {
Register(t, pred, fn, fromPython);
} else {
TF_CODING_ERROR("Cannot register unknown TfType: %s.", ArchGetDemangled<T>().c_str());
}
}

/// \brief Wraps \p fn in a ReaderFactoryFn and registers that factory
/// function as a reader provider for \p T.
/// This is a helper method for the macro PXRUSDMAYA_DEFINE_READER;
Expand All @@ -100,13 +138,19 @@ struct UsdMayaPrimReaderRegistry
/// prim.GetTypeName()
/// \endcode
MAYAUSD_CORE_PUBLIC
static ReaderFactoryFn Find(const TfToken& usdTypeName);
static ReaderFactoryFn Find(
const TfToken& usdTypeName,
const UsdMayaJobImportArgs& importArgs,
const UsdPrim& importPrim);

/// Similar to Find(), but returns a "fallback" prim reader factory if none
/// can be found for \p usdTypeName. Thus, this always returns a valid
/// reader factory.
MAYAUSD_CORE_PUBLIC
static ReaderFactoryFn FindOrFallback(const TfToken& usdTypeName);
static ReaderFactoryFn FindOrFallback(
const TfToken& usdTypeName,
const UsdMayaJobImportArgs& importArgs,
const UsdPrim& importPrim);
};

// Lookup TfType by name instead of static C++ type when
Expand Down
7 changes: 6 additions & 1 deletion lib/mayaUsd/fileio/primUpdater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ bool UsdMayaPrimUpdater::canEditAsMaya() const
// Invalid prim cannot be edited.
if (!prim)
return false;
return (UsdMayaPrimReaderRegistry::Find(prim.GetTypeName()) != nullptr);

UsdMayaJobImportArgs jobArgs = UsdMayaJobImportArgs::CreateFromDictionary(
_context->GetUserArgs(),
/* importWithProxyShapes = */ false,
GfInterval::GetFullInterval());
return (UsdMayaPrimReaderRegistry::Find(prim.GetTypeName(), jobArgs, prim) != nullptr);
}

bool UsdMayaPrimUpdater::editAsMaya() { return true; }
Expand Down
13 changes: 0 additions & 13 deletions lib/mayaUsd/fileio/shaderReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,6 @@ class UsdMayaShaderReader : public UsdMayaPrimReader
MAYAUSD_CORE_PUBLIC
UsdMayaShaderReader(const UsdMayaPrimReaderArgs&);

/// The level of support a reader can offer for a given context
///
/// A basic reader that gives correct results across most contexts should
/// report `Fallback`, while a specialized reader that really shines in a
/// given context should report `Supported` when the context is right and
/// `Unsupported` if the context is not as expected.
enum class ContextSupport
{
Supported,
Fallback,
Unsupported
};

/// This static function is expected for all shader readers and allows
/// declaring how well this class can support the current context:
MAYAUSD_CORE_PUBLIC
Expand Down
2 changes: 1 addition & 1 deletion plugin/pxr/maya/lib/usdMaya/readJob_ImportWithProxies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ bool UsdMaya_ReadJobWithSceneAssembly::_ProcessCameraPrims(const std::vector<Usd
}

if (UsdMayaPrimReaderRegistry::ReaderFactoryFn factoryFn
= UsdMayaPrimReaderRegistry::Find(cameraPrim.GetTypeName())) {
= UsdMayaPrimReaderRegistry::Find(cameraPrim.GetTypeName(), mArgs, cameraPrim)) {
UsdMayaPrimReaderSharedPtr primReader = factoryFn(args);
if (primReader) {
primReader->Read(ctx);
Expand Down