diff --git a/pxr/usd/usdMtlx/parser.cpp b/pxr/usd/usdMtlx/parser.cpp index e009159303..3d39a6998e 100644 --- a/pxr/usd/usdMtlx/parser.cpp +++ b/pxr/usd/usdMtlx/parser.cpp @@ -48,6 +48,14 @@ TF_DEFINE_PRIVATE_TOKENS( ((discoveryType, "mtlx")) ((sourceType, "")) + + (uimin) + (uimax) + (uisoftmin) + (uisoftmax) + (uistep) + (unit) + (unittype) ); // This environment variable lets users override the name of the primary @@ -124,6 +132,73 @@ struct ShaderBuilder { std::map _propertyNameRemapping; }; +static +void +ParseMetadata( + NdrTokenMap& metadata, + const TfToken& key, + const mx::ConstElementPtr& element, + const std::string& attribute) +{ + const auto& value = element->getAttribute(attribute); + if (!value.empty()) { + metadata.emplace(key, value); + } +} + +static +void +ParseOptions( + NdrOptionVec& options, + const mx::ConstElementPtr& element +) +{ + const auto& enumLabels = element->getAttribute("enum"); + if (enumLabels.empty()) { + return; + } + + const auto& enumValues = element->getAttribute("enumvalues"); + std::vector allLabels = UsdMtlxSplitStringArray(enumLabels); + std::vector allValues = UsdMtlxSplitStringArray(enumValues); + + if (allValues.size() && allValues.size() != allLabels.size()) { + // An array of vector2 values will produce twice the expected number of + // elements. We can fix that by regrouping them. + if (allValues.size() > allLabels.size() && + allValues.size() % allLabels.size() == 0) { + + size_t stride = allValues.size() / allLabels.size(); + std::vector rebuiltValues; + std::string currentValue; + for (size_t i = 0; i < allValues.size(); ++i) { + if (i % stride != 0) { + currentValue += mx::ARRAY_PREFERRED_SEPARATOR; + } + currentValue += allValues[i]; + if ((i+1) % stride == 0) { + rebuiltValues.push_back(currentValue); + currentValue = ""; + } + } + allValues.swap(rebuiltValues); + } else { + // Can not reconcile the size difference: + allValues.clear(); + } + } + + auto itLabels = allLabels.cbegin(); + auto itValues = allValues.cbegin(); + while (itLabels != allLabels.cend()) { + TfToken value; + if (itValues != allValues.cend()) { + value = TfToken(*itValues++); + } + options.emplace_back(TfToken(*itLabels++), value); + } +} + void ShaderBuilder::AddProperty( const mx::ConstTypedElementPtr& element, @@ -242,6 +317,36 @@ ShaderBuilder::AddProperty( metadata[SdrPropertyMetadata->ImplementationName] = j->second; } + if (!isOutput) { + ParseMetadata(metadata, SdrPropertyMetadata->Label, element, "uiname"); + ParseMetadata(metadata, SdrPropertyMetadata->Help, element, "doc"); + ParseMetadata(metadata, SdrPropertyMetadata->Page, element, "uifolder"); + + ParseMetadata(metadata, _tokens->uimin, element, "uimin"); + ParseMetadata(metadata, _tokens->uimax, element, "uimax"); + ParseMetadata(metadata, _tokens->uisoftmin, element, "uisoftmin"); + ParseMetadata(metadata, _tokens->uisoftmax, element, "uisoftmax"); + ParseMetadata(metadata, _tokens->uistep, element, "uistep"); + ParseMetadata(metadata, _tokens->unit, element, "unit"); + ParseMetadata(metadata, _tokens->unittype, element, "unittype"); + + for (const auto& pair : metadata) { + const TfToken attrName = pair.first; + const std::string attrValue = pair.second; + + if (std::find(SdrPropertyMetadata->allTokens.begin(), + SdrPropertyMetadata->allTokens.end(), + attrName) != SdrPropertyMetadata->allTokens.end()){ + continue; + } + + // Attribute hasn't been handled yet, so put it into the hints dict + hints.insert({attrName, attrValue}); + } + + ParseOptions(options, element); + } + // Add the property. properties.push_back( SdrShaderPropertyUniquePtr( diff --git a/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.py b/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.py index dba942f8d2..fbfd4b4bd1 100644 --- a/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.py +++ b/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.py @@ -57,6 +57,40 @@ def test_NodeParser(self): self.assertEqual(sorted(node.GetInputNames()), ["in", "note"]) self.assertEqual(node.GetOutputNames(), ['out']) + # Verify some metadata: + node = Sdr.Registry().GetShaderNodeByIdentifier( + 'UsdMtlxTestNamespace:nd_vector') + self.assertEqual(node.GetHelp(), "Vector help") + # Properties without a Page metadata end up in an unnamed page. This + # means that all MaterialX outputs will be assigned to the unnamed page + # when the metadata is used. + self.assertEqual(node.GetPages(), ["UI Page", ""]) + self.assertEqual(node.GetPropertyNamesForPage("UI Page"), ["in",]) + self.assertEqual(node.GetPropertyNamesForPage(""), ["note", "out"]) + input = node.GetInput("in") + self.assertEqual(input.GetHelp(), "Property help") + self.assertEqual(input.GetLabel(), "UI Vector") + self.assertEqual(input.GetPage(), "UI Page") + self.assertEqual(input.GetOptions(), + [("X", "1, 0, 0"), ("Y", "0, 1, 0"), ("Z", "0, 0, 1")]) + + node = Sdr.Registry().GetShaderNodeByIdentifier( + 'UsdMtlxTestNamespace:nd_float') + expected = { + "uimin": "-360.0", + "uimax": "360.0", + "uisoftmin": "0.0", + "uisoftmax": "180.0", + "uistep": "1.0", + "unittype": "angle", + "unit": "degree" + } + hints = node.GetInput("in").GetHints() + metadata = node.GetInput("in").GetMetadata() + for key in expected.keys(): + self.assertEqual(hints[key], expected[key]) + self.assertEqual(metadata[key], expected[key]) + # Verify converted types. typeNameMap = { 'boolean': 'bool', diff --git a/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.testenv/test.mtlx b/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.testenv/test.mtlx index c908bb503d..540a2649a7 100644 --- a/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.testenv/test.mtlx +++ b/pxr/usd/usdMtlx/testenv/testUsdMtlxParser.testenv/test.mtlx @@ -6,7 +6,7 @@ - + @@ -15,8 +15,8 @@ - - + +