Skip to content

Commit

Permalink
Merge pull request #1641 from kxl-adsk/kxl-adsk/fix_materialx_nodedef…
Browse files Browse the repository at this point in the history
…_discovery

Fix MaterialX Nodedef Discovery

(Internal change: 2205714)
  • Loading branch information
pixar-oss committed Dec 17, 2021
2 parents 6ad61d3 + 08a5644 commit 4ac8a7e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 152 deletions.
113 changes: 57 additions & 56 deletions pxr/usd/plugin/usdMtlx/discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,55 @@ 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
_MapNodeNamesToBaseForVersioning(mx::ConstElementPtr mtlx, _NameMapping* mapping)
{
static const std::string inheritAttr("inherit");

// Find shortest:
const std::string* shortestName = &mtlx->getName();
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:
auto r = mapping->emplace(mtlx->getName(), *shortestName);
// If shortestName is shorter than the existing name, replace it.
if (!r.second && shortestName->size() < r.first->second.size()) {
r.first->second = *shortestName;
}
while (true) {
const std::string& inherit = mtlx->getAttribute(inheritAttr);
if (inherit.empty()) {
break;
}
if (auto inherited = mtlx->getRoot()->getChild(inherit)) {
mtlx = inherited;
auto r = mapping->emplace(mtlx->getName(), *shortestName);
// If shortestName is shorter than the existing name, replace it.
if (!r.second && shortestName->size() < r.first->second.size()) {
r.first->second = *shortestName;
}
}
else {
break;
}
}
return mtlx->getName();
}

// Choose an Ndr name based on compatible MaterialX nodedef names.
Expand All @@ -74,13 +104,27 @@ _ComputeNameMapping(const mx::ConstDocumentPtr& doc)
{
_NameMapping result;

// We use the simple heuristic of using the name of the top-most
// 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.
// For each nodeDef with an inheritance chain, we populate the
// _NameMapping with the shortest name found in the inheritance
// hierarchy
//
// 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()) {
_MapNodeNamesToBaseForVersioning(mtlxNodeDef, &result);
}
}

return result;
Expand All @@ -104,48 +148,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 @@ -155,10 +159,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

0 comments on commit 4ac8a7e

Please sign in to comment.