diff --git a/pxr/usd/plugin/usdMtlx/discovery.cpp b/pxr/usd/plugin/usdMtlx/discovery.cpp index ab018249b1..ee1d5e65c3 100644 --- a/pxr/usd/plugin/usdMtlx/discovery.cpp +++ b/pxr/usd/plugin/usdMtlx/discovery.cpp @@ -46,12 +46,33 @@ TF_DEFINE_PRIVATE_TOKENS( // Maps a nodedef name to its NdrNode name. using _NameMapping = std::map; -// 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 = ¤t->getName(); + } + } + else { + break; + } + } + // Populate mapping: + mappping.emplace(mtlx->getName(), *shortestName); while (true) { const std::string& inherit = mtlx->getAttribute(inheritAttr); if (inherit.empty()) { @@ -59,12 +80,12 @@ _GetTopMostAncestralName(mx::ConstElementPtr mtlx) } 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. @@ -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; @@ -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()), @@ -154,10 +150,7 @@ _DiscoverNodes( fileResult.discoveryType, fileResult.sourceType, fileResult.uri, - fileResult.resolvedUri, - /* sourceCode */ "", - /* metadata */ NdrTokenMap(), - /* blindData */ nodeGraph->getName() + fileResult.resolvedUri ); } } diff --git a/pxr/usd/plugin/usdMtlx/parser.cpp b/pxr/usd/plugin/usdMtlx/parser.cpp index 54993b9fa7..9db619d71c 100644 --- a/pxr/usd/plugin/usdMtlx/parser.cpp +++ b/pxr/usd/plugin/usdMtlx/parser.cpp @@ -341,10 +341,11 @@ 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); } @@ -352,78 +353,6 @@ ParseElement(ShaderBuilder* builder, const mx::ConstNodeDefPtr& nodeDef) 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. @@ -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()) { - ParseElement(&builder, nodeGraph, discoveryResult); - } - else if (auto impl = element->asA()) { - 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(); }