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

Fix MaterialX Nodedef Discovery #1641

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
97 changes: 45 additions & 52 deletions pxr/usd/plugin/usdMtlx/discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,46 @@ TF_DEFINE_PRIVATE_TOKENS(
// Maps a nodedef name to its NdrNode name.
using _NameMapping = std::map<std::string, std::string>;

// Return the name of the most-ancestral element.
const std::string&
_GetTopMostAncestralName(mx::ConstElementPtr mtlx)
// Fill the name mapping with the shortest name found in the inheritance
// hierarchy:
void _FindAncestralMappings(mx::ConstElementPtr mtlx, _NameMapping& mappping)
{
static const std::string inheritAttr("inherit");

const std::string* shortestName = &mtlx->getName();

// Find shortest:
mx::ConstElementPtr current = mtlx;
while (true) {
const std::string& inherit = current->getAttribute(inheritAttr);
if (inherit.empty()) {
break;
}
if (auto inherited = current->getRoot()->getChild(inherit)) {
current = inherited;
if (current->getName().size() < shortestName->size()) {
shortestName = &current->getName();
}
}
else {
break;
}
}
// Populate mapping:
mappping.emplace(mtlx->getName(), *shortestName);
while (true) {
const std::string& inherit = mtlx->getAttribute(inheritAttr);
if (inherit.empty()) {
break;
}
if (auto inherited = mtlx->getRoot()->getChild(inherit)) {
mtlx = inherited;
mappping.emplace(mtlx->getName(), *shortestName);
}
else {
break;
}
}
return mtlx->getName();
}

// Choose an Ndr name based on compatible MaterialX nodedef names.
Expand All @@ -77,9 +98,24 @@ _ComputeNameMapping(const mx::ConstDocumentPtr& doc)
// nodedef on the inheritance chain where top-most is the one
// that doesn't itself inherit anything. The 1.36 spec gives
// guidance that this should be sufficient.
//
// mix_float_210 (v2.1)
// inherits mix_float_200 (v2.0)
// inherits mix_float (original version)
//
// A versioning inheritance can also choose to keep the latest version with
// the official name, and tag the earlier versions:
//
// mix_float (v2.1 latest)
// inherits mix_float_200 (v2.0)
// inherits mix_float_100 (v1.0)
//
// So we need to traverse the hierarchy, and at each point pick the
// shortest name.
for (auto&& mtlxNodeDef: doc->getNodeDefs()) {
result.emplace(mtlxNodeDef->getName(),
_GetTopMostAncestralName(mtlxNodeDef));
if (mtlxNodeDef->hasInheritString()) {
_FindAncestralMappings(mtlxNodeDef, result);
}
}

return result;
Expand All @@ -103,48 +139,8 @@ _DiscoverNodes(
{
static const TfToken family = TfToken();

// MaterialX allows nodes definitions through implementation
// and nodegraph elements. We scan the file for those,
// discarding any that don't refer to node definitions, and
// insert into the discovery result list.

// Get the implementations.
for (auto&& impl: doc->getImplementations()) {
auto&& nodeDef = impl->getNodeDef();
if (!nodeDef) {
continue;
}

// Ignore implementations that don't refer to a file.
// XXX -- Do we want to allow these? The renderer will
// be expected to provide the implementation.
if (impl->getFile().empty()) {
continue;
}

bool implicitDefault;
result->emplace_back(
NdrIdentifier(nodeDef->getName()),
UsdMtlxGetVersion(nodeDef, &implicitDefault),
_ChooseName(nodeDef->getName(), nameMapping),
TfToken(nodeDef->getNodeString()),
fileResult.discoveryType,
fileResult.sourceType,
fileResult.uri,
fileResult.resolvedUri,
/* sourceCode */ "",
/* metadata */ NdrTokenMap(),
/* blindData */ impl->getName()
);
}

// Get the nodegraphs implementing node defs.
for (auto&& nodeGraph: doc->getNodeGraphs()) {
auto&& nodeDef = nodeGraph->getNodeDef();
if (!nodeDef) {
continue;
}

// Get the node definitions
for (auto&& nodeDef: doc->getNodeDefs()) {
bool implicitDefault;
result->emplace_back(
NdrIdentifier(nodeDef->getName()),
Expand All @@ -154,10 +150,7 @@ _DiscoverNodes(
fileResult.discoveryType,
fileResult.sourceType,
fileResult.uri,
fileResult.resolvedUri,
/* sourceCode */ "",
/* metadata */ NdrTokenMap(),
/* blindData */ nodeGraph->getName()
fileResult.resolvedUri
);
}
}
Expand Down
104 changes: 8 additions & 96 deletions pxr/usd/plugin/usdMtlx/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,89 +341,18 @@ ParseElement(ShaderBuilder* builder, const mx::ConstNodeDefPtr& nodeDef)
}

// Properties
for (const auto& mtlxInput: nodeDef->getInputs()) {
for (const auto& mtlxInput: nodeDef->getActiveInputs()) {
builder->AddProperty(mtlxInput, false, &primvars);
}
for (const auto& mtlxOutput: nodeDef->getOutputs()) {

for (const auto& mtlxOutput: nodeDef->getActiveOutputs()) {
builder->AddProperty(mtlxOutput, true, nullptr);
}

builder->metadata[SdrNodeMetadata->Primvars] =
TfStringJoin(primvars.begin(), primvars.end(), "|");
}

static
void
ParseElement(
ShaderBuilder* builder,
const mx::ConstNodeGraphPtr& nodeGraph,
const NdrNodeDiscoveryResult& discoveryResult)
{
ParseElement(builder, nodeGraph->getNodeDef());
if (*builder) {
// XXX -- Node graphs not supported yet.
}
}

static
void
ParseElement(
ShaderBuilder* builder,
const mx::ConstImplementationPtr& impl,
const NdrNodeDiscoveryResult& discoveryResult)
{
// Name remapping.
for (const auto& mtlxInput: impl->getInputs()) {
builder->AddPropertyNameRemapping(
mtlxInput->getName(),
mtlxInput->getAttribute("implname"));
}

ParseElement(builder, impl->getNodeDef());
if (!*builder) {
return;
}

// Get the implementation file. Note we're not doing proper Ar asset
// localization here yet.
auto filename = impl->getFile();
if (filename.empty()) {
builder->SetInvalid();
return;
}

if (TfIsRelativePath(filename)) {
// The path is relative to some library path but we don't know which.
// We'll just check them all until we find an existing file.
// XXX -- Since we're likely to do this with every implementation
// element we should consider some kind of cache so we don't
// keep hitting the filesystem.
// XXX -- A future version of the asset resolver that has protocols
// would make it easy for clients to resolve a relative path.
// We should switch to that when available.
for (const auto& dir: UsdMtlxStandardLibraryPaths()) {
const auto path = TfStringCatPaths(dir, filename);
if (TfIsFile(path, true)) {
filename = path;
break;
}
}
if (TfIsRelativePath(filename)) {
TF_DEBUG(NDR_PARSING).Msg("MaterialX implementation %s could "
"not be found", filename.c_str());
builder->SetInvalid();
return;
}
}
builder->implementationURI = filename;

// Function
const auto& function = impl->getFunction();
if (!function.empty()) {
builder->metadata[SdrNodeMetadata->ImplementationName] = function;
}
}

} // anonymous namespace

/// Parses nodes in MaterialX files.
Expand Down Expand Up @@ -465,32 +394,15 @@ UsdMtlxParserPlugin::Parse(
return GetInvalidNode(discoveryResult);
}

// Get the element.
if (discoveryResult.blindData.empty()) {
TF_WARN("Invalid MaterialX blindData; should have node name");
return GetInvalidNode(discoveryResult);
}

auto element = document->getChild(discoveryResult.blindData);
if (!element) {
TF_WARN("Invalid MaterialX blindData; unknown node name ' %s '",
discoveryResult.blindData.c_str());
auto nodeDef = document->getNodeDef(discoveryResult.identifier.GetString());
if (!nodeDef) {
TF_WARN("Invalid MaterialX NodeDef; unknown node name ' %s '",
discoveryResult.identifier.GetText());
return GetInvalidNode(discoveryResult);
}

// Handle nodegraphs and implementations differently.
ShaderBuilder builder(discoveryResult);
if (auto nodeGraph = element->asA<mx::NodeGraph>()) {
ParseElement(&builder, nodeGraph, discoveryResult);
}
else if (auto impl = element->asA<mx::Implementation>()) {
ParseElement(&builder, impl, discoveryResult);
}
else {
TF_VERIFY(false,
"MaterialX node '%s' isn't a nodegraph or implementation",
element->getNamePath().c_str());
}
ParseElement(&builder, nodeDef);

return builder.Build();
}
Expand Down