1 //
2 // Copyright 2020 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/imaging/hdSt/materialXFilter.h"
25 #include "pxr/imaging/hdSt/materialXShaderGen.h"
26 #include "pxr/imaging/hdMtlx/hdMtlx.h"
27 
28 #include "pxr/usd/sdr/registry.h"
29 #include "pxr/imaging/hio/glslfx.h"
30 
31 #include "pxr/base/gf/vec2f.h"
32 #include "pxr/base/gf/matrix3d.h"
33 #include "pxr/base/gf/matrix4d.h"
34 
35 #include "pxr/base/tf/diagnostic.h"
36 
37 #include <MaterialXGenShader/Util.h>
38 #include <MaterialXGenShader/Shader.h>
39 #include <MaterialXRender/Util.h>
40 #include <MaterialXRender/LightHandler.h>
41 
42 namespace mx = MaterialX;
43 
44 PXR_NAMESPACE_OPEN_SCOPE
45 
46 
47 TF_DEFINE_PRIVATE_TOKENS(
48     _tokens,
49     (mtlx)
50 
51     // Default Texture Coordinate Token
52     (st)
53     (texcoord)
54     (geomprop)
55 
56     // Opacity Parameters
57     (opacity)
58     (opacityThreshold)
59     (transmission)
60 );
61 
62 
63 ////////////////////////////////////////////////////////////////////////////////
64 // Shader Gen Functions
65 
66 // Generate the Glsl Pixel Shader based on the given mxContext and mxElement
67 // Based on MaterialXViewer Material::generateShader()
68 static std::string
_GenPixelShader(mx::GenContext & mxContext,mx::ElementPtr const & mxElem)69 _GenPixelShader(mx::GenContext & mxContext, mx::ElementPtr const& mxElem)
70 {
71     bool hasTransparency = mx::isTransparentSurface(mxElem,
72                                 mxContext.getShaderGenerator());
73 
74     mx::GenContext materialContext = mxContext;
75     materialContext.getOptions().hwTransparency = hasTransparency;
76     materialContext.getOptions().hwShadowMap =
77         materialContext.getOptions().hwShadowMap && !hasTransparency;
78 
79     // Use the domeLightPrefilter texture instead of sampling the Environment Map
80     materialContext.getOptions().hwSpecularEnvironmentMethod =
81         mx::HwSpecularEnvironmentMethod::SPECULAR_ENVIRONMENT_PREFILTER;
82 
83     mx::ShaderPtr mxShader = mx::createShader("Shader", materialContext, mxElem);
84     if (mxShader) {
85         return mxShader->getSourceCode(mx::Stage::PIXEL);
86     }
87     return mx::EMPTY_STRING;
88 }
89 
90 // Results in lightData.type = 1 for point lights in the Mx Shader
91 static const std::string mxDirectLightString =
92 R"(
93 <?xml version="1.0"?>
94 <materialx version="1.37">
95   <point_light name="pt_light" type="lightshader">
96   </point_light>
97 </materialx>
98 )";
99 
100 // Use the given mxDocument to generate the corresponding glsl source code
101 // Based on MaterialXViewer Viewer::loadDocument()
102 std::string
HdSt_GenMaterialXShaderCode(mx::DocumentPtr const & mxDoc,mx::FileSearchPath const & searchPath,MxHdInfo const & mxHdInfo)103 HdSt_GenMaterialXShaderCode(
104     mx::DocumentPtr const& mxDoc,
105     mx::FileSearchPath const& searchPath,
106     MxHdInfo const& mxHdInfo)
107 {
108     // Initialize the Context for shaderGen.
109     mx::GenContext mxContext = HdStMaterialXShaderGen::create(mxHdInfo);
110     mxContext.registerSourceCodeSearchPath(searchPath);
111 
112     // Add the Direct Light mtlx file to the mxDoc
113     mx::DocumentPtr lightDoc = mx::createDocument();
114     mx::readFromXmlString(lightDoc, mxDirectLightString);
115     mxDoc->importLibrary(lightDoc);
116 
117     // Make sure the Light data properties are added to the mxLightData struct
118     mx::LightHandler lightHandler;
119     std::vector<mx::NodePtr> lights;
120     lightHandler.findLights(mxDoc, lights);
121     lightHandler.registerLights(mxDoc, lights, mxContext);
122 
123     // Find renderable elements in the Mtlx Document.
124     std::vector<mx::TypedElementPtr> renderableElements;
125     mx::findRenderableElements(mxDoc, renderableElements);
126 
127     // Should have exactly one renderable element (material).
128     if (renderableElements.size() != 1) {
129         TF_CODING_ERROR("Generated MaterialX Document does not "
130                         "have 1 material");
131         return mx::EMPTY_STRING;
132     }
133 
134     // Extract out the Surface Shader Node for the Material Node
135     mx::TypedElementPtr renderableElem = renderableElements.at(0);
136     mx::NodePtr node = renderableElem->asA<mx::Node>();
137     if (node && node->getType() == mx::MATERIAL_TYPE_STRING) {
138         std::unordered_set<mx::NodePtr> mxShaderNodes;
139         mxShaderNodes = mx::getShaderNodes(node, mx::SURFACE_SHADER_TYPE_STRING);
140         if (!mxShaderNodes.empty()) {
141             renderableElem = *mxShaderNodes.begin();
142         }
143     }
144     // Generate the PixelShader for the renderable element (surfaceshader).
145     const mx::ElementPtr & mxElem = mxDoc->getDescendant(
146                                             renderableElem->getNamePath());
147     mx::TypedElementPtr typedElem = mxElem ? mxElem->asA<mx::TypedElement>()
148                                          : nullptr;
149     if (typedElem) {
150         return _GenPixelShader(mxContext, typedElem);
151     }
152     return mx::EMPTY_STRING;
153 }
154 
155 
156 ////////////////////////////////////////////////////////////////////////////////
157 // Helper Functions to convert MX texture node parameters to Hd parameters
158 
159 // Get the Hydra VtValue for the given MaterialX input value
160 static VtValue
_GetHdFilterValue(std::string const & mxInputValue)161 _GetHdFilterValue(std::string const& mxInputValue)
162 {
163     if(mxInputValue == "closest") {
164         return VtValue(HdStTextureTokens->nearestMipmapNearest);
165     }
166     return VtValue(HdStTextureTokens->linearMipmapLinear);
167 }
168 
169 // Get the Hydra VtValue for the given MaterialX input value
170 static VtValue
_GetHdSamplerValue(std::string const & mxInputValue)171 _GetHdSamplerValue(std::string const& mxInputValue)
172 {
173     if (mxInputValue == "constant") {
174         return VtValue(HdStTextureTokens->black);
175     }
176     if (mxInputValue == "clamp") {
177         return VtValue(HdStTextureTokens->clamp);
178     }
179     if (mxInputValue == "mirror") {
180         return VtValue(HdStTextureTokens->mirror);
181     }
182     return VtValue(HdStTextureTokens->repeat);
183 }
184 
185 // Translate the MaterialX texture node input into the Hydra equivalents
186 static void
_GetHdTextureParameters(std::string const & mxInputName,std::string const & mxInputValue,std::map<TfToken,VtValue> * hdTextureParams)187 _GetHdTextureParameters(
188     std::string const& mxInputName,
189     std::string const& mxInputValue,
190     std::map<TfToken, VtValue>* hdTextureParams)
191 {
192     // MaterialX has two texture2d node types <image> and <tiledimage>
193 
194     // Properties common to both <image> and <tiledimage> texture nodes:
195     if (mxInputName == "filtertype") {
196         (*hdTextureParams)[HdStTextureTokens->minFilter] =
197             _GetHdFilterValue(mxInputValue);
198         (*hdTextureParams)[HdStTextureTokens->magFilter] =
199             VtValue(HdStTextureTokens->linear);
200     }
201 
202     // Properties specific to <image> nodes:
203     else if (mxInputName == "uaddressmode") {
204         (*hdTextureParams)[HdStTextureTokens->wrapS] =
205             _GetHdSamplerValue(mxInputValue);
206     }
207     else if (mxInputName == "vaddressmode") {
208         (*hdTextureParams)[HdStTextureTokens->wrapT] =
209             _GetHdSamplerValue(mxInputValue);
210     }
211 
212     // Properties specific to <tiledimage> nodes:
213     else if (mxInputName == "uvtiling" || mxInputName == "uvoffset" ||
214         mxInputName == "realworldimagesize" ||
215         mxInputName == "realworldtilesize") {
216         (*hdTextureParams)[HdStTextureTokens->wrapS] =
217             VtValue(HdStTextureTokens->repeat);
218         (*hdTextureParams)[HdStTextureTokens->wrapT] =
219             VtValue(HdStTextureTokens->repeat);
220     }
221 }
222 
223 // Find the HdNode and its corresponding NodePath in the given HdNetwork
224 // based on the given HdConnection
225 static bool
_FindConnectedNode(HdMaterialNetwork2 const & hdNetwork,HdMaterialConnection2 const & hdConnection,HdMaterialNode2 * hdNode,SdfPath * hdNodePath)226 _FindConnectedNode(
227     HdMaterialNetwork2 const& hdNetwork,
228     HdMaterialConnection2 const& hdConnection,
229     HdMaterialNode2* hdNode,
230     SdfPath* hdNodePath)
231 {
232     // Get the path to the connected node
233     const SdfPath & connectionPath = hdConnection.upstreamNode;
234 
235     // If this path is not in the network raise a warning
236     auto hdNodeIt = hdNetwork.nodes.find(connectionPath);
237     if (hdNodeIt == hdNetwork.nodes.end()) {
238         TF_WARN("Unknown material node '%s'", connectionPath.GetText());
239         return false;
240     }
241 
242     // Otherwise return the HdNode and corresponding NodePath
243     *hdNode = hdNodeIt->second;
244     *hdNodePath = connectionPath;
245     return true;
246 }
247 
248 // Get the Texture coordinate name if specified, otherwise get the default name
249 static void
_GetTextureCoordinateName(mx::DocumentPtr const & mxDoc,HdMaterialNetwork2 * hdNetwork,HdMaterialNode2 * hdTextureNode,SdfPath const & hdTextureNodePath,mx::StringMap * mxHdPrimvarMap,std::string * defaultTexcoordName)250 _GetTextureCoordinateName(
251     mx::DocumentPtr const &mxDoc,
252     HdMaterialNetwork2* hdNetwork,
253     HdMaterialNode2* hdTextureNode,
254     SdfPath const& hdTextureNodePath,
255     mx::StringMap* mxHdPrimvarMap,
256     std::string* defaultTexcoordName)
257 {
258     // Get the Texture Coordinate name through the connected node
259     bool textureCoordSet = false;
260     for (auto& inputConnections : hdTextureNode->inputConnections) {
261 
262         // Texture Coordinates are connected through the 'texcoord' input
263         if ( inputConnections.first != _tokens->texcoord) {
264             continue;
265         }
266 
267         for (auto& currConnection : inputConnections.second) {
268 
269             // Get the connected Texture Coordinate node
270             SdfPath hdCoordNodePath;
271             HdMaterialNode2 hdCoordNode;
272             const bool found = _FindConnectedNode(*hdNetwork, currConnection,
273                                             &hdCoordNode, &hdCoordNodePath);
274             if (!found) {
275                 continue;
276             }
277 
278             // Get the texture coordinate name from the 'geomprop' parameter
279             auto coordNameIt = hdCoordNode.parameters.find(_tokens->geomprop);
280             if (coordNameIt != hdCoordNode.parameters.end()) {
281 
282                 std::string const& texcoordName =
283                     HdMtlxConvertToString(coordNameIt->second);
284 
285                 // Set the 'st' parameter as a TfToken
286                 // hdTextureNode->parameters[_tokens->st] =
287                 hdNetwork->nodes[hdTextureNodePath].parameters[_tokens->st] =
288                                 TfToken(texcoordName.c_str());
289 
290                 // Save texture coordinate primvar name for the glslfx header;
291                 // figure out the mx typename
292                 mx::NodeDefPtr mxNodeDef = mxDoc->getNodeDef(
293                         hdCoordNode.nodeTypeId.GetString());
294                 if (mxNodeDef) {
295                     (*mxHdPrimvarMap)[texcoordName] = mxNodeDef->getType();
296                     textureCoordSet = true;
297                     break;
298                 }
299             }
300         }
301     }
302 
303     // If we did not have a connected node, and the 'st' parameter is not set
304     // get the default texture cordinate name from the textureNodes sdr metadata
305     if ( !textureCoordSet && hdTextureNode->parameters.find(_tokens->st) ==
306                              hdTextureNode->parameters.end()) {
307 
308         // Get the sdr node for the mxTexture node
309         SdrRegistry &sdrRegistry = SdrRegistry::GetInstance();
310         const SdrShaderNodeConstPtr sdrTextureNode =
311             sdrRegistry.GetShaderNodeByIdentifierAndType(
312                 hdTextureNode->nodeTypeId, _tokens->mtlx);
313 
314         if (sdrTextureNode) {
315 
316             // Get the primvarname from the sdrTextureNode metadata
317             auto metadata = sdrTextureNode->GetMetadata();
318             auto primvarName = metadata[SdrNodeMetadata->Primvars];
319 
320             // Set the 'st' parameter as a TfToken
321             hdNetwork->nodes[hdTextureNodePath].parameters[_tokens->st] =
322                             TfToken(primvarName.c_str());
323 
324             // Save the default texture coordinate name for the glslfx header
325             *defaultTexcoordName = primvarName;
326         }
327 
328     }
329 }
330 
331 // Add the Hydra texture node parameters to the texture nodes and connect the
332 // texture nodes to the terminal node
333 static void
_UpdateTextureNodes(mx::DocumentPtr const & mxDoc,HdMaterialNetwork2 * hdNetwork,SdfPath const & hdTerminalNodePath,std::set<SdfPath> const & hdTextureNodes,mx::StringMap * mxHdTextureMap,mx::StringMap * mxHdPrimvarMap,std::string * defaultTexcoordName)334 _UpdateTextureNodes(
335     mx::DocumentPtr const &mxDoc,
336     HdMaterialNetwork2* hdNetwork,
337     SdfPath const& hdTerminalNodePath,
338     std::set<SdfPath> const& hdTextureNodes,
339     mx::StringMap* mxHdTextureMap,
340     mx::StringMap* mxHdPrimvarMap,
341     std::string* defaultTexcoordName)
342 {
343     for (auto const& texturePath : hdTextureNodes) {
344         HdMaterialNode2 hdTextureNode = hdNetwork->nodes[texturePath];
345 
346         _GetTextureCoordinateName(mxDoc, hdNetwork, &hdTextureNode, texturePath,
347                                   mxHdPrimvarMap, defaultTexcoordName);
348 
349         // Gather the Hydra Texture Parameters
350         std::map<TfToken, VtValue> hdParameters;
351         for (auto const& currParam : hdTextureNode.parameters) {
352 
353             // Get the MaterialX Input Value string
354             std::string mxInputValue = HdMtlxConvertToString(currParam.second);
355 
356             // Get the Hydra equivalents for the MX Texture node parameters
357             std::string mxInputName = currParam.first.GetText();
358             _GetHdTextureParameters(mxInputName, mxInputValue, &hdParameters);
359         }
360 
361         // Add the Hydra Texture Parameters to the Texture Node
362         for (auto param : hdParameters) {
363             hdNetwork->nodes[texturePath].parameters[param.first] = param.second;
364         }
365 
366         // Connect the texture node to the terminal node for HdStMaterialNetwork
367         // Create a unique name for the new connection, and update the
368         // mxHdTextureMap with this connection name so Hydra's codegen and
369         // HdStMaterialXShaderGen match up correctly
370         std::string newConnName = texturePath.GetName() + "_" +
371                                 (*mxHdTextureMap)[texturePath.GetName()];;
372         (*mxHdTextureMap)[texturePath.GetName()] = newConnName;
373 
374         // Make and add a new connection to the terminal node
375         HdMaterialConnection2 textureConn;
376         textureConn.upstreamNode = texturePath;
377         textureConn.upstreamOutputName = TfToken(newConnName);
378         hdNetwork->nodes[hdTerminalNodePath].
379             inputConnections[textureConn.upstreamOutputName] = {textureConn};
380     }
381 }
382 
383 // Connect the primvar nodes to the terminal node
384 static void
_UpdatePrimvarNodes(mx::DocumentPtr const & mxDoc,HdMaterialNetwork2 * hdNetwork,SdfPath const & hdTerminalNodePath,std::set<SdfPath> const & hdPrimvarNodes,mx::StringMap * mxHdPrimvarMap)385 _UpdatePrimvarNodes(
386     mx::DocumentPtr const &mxDoc,
387     HdMaterialNetwork2* hdNetwork,
388     SdfPath const& hdTerminalNodePath,
389     std::set<SdfPath> const& hdPrimvarNodes,
390     mx::StringMap* mxHdPrimvarMap)
391 {
392     for (auto const& primvarPath : hdPrimvarNodes) {
393         HdMaterialNode2 hdPrimvarNode = hdNetwork->nodes[primvarPath];
394 
395         // Save primvar name for the glslfx header
396         auto primvarNameIt = hdPrimvarNode.parameters.find(_tokens->geomprop);
397         if (primvarNameIt != hdPrimvarNode.parameters.end()) {
398             std::string const& primvarName =
399                 HdMtlxConvertToString(primvarNameIt->second);
400 
401             // Figure out the mx typename
402             mx::NodeDefPtr mxNodeDef = mxDoc->getNodeDef(
403                     hdPrimvarNode.nodeTypeId.GetString());
404             if (mxNodeDef) {
405                 (*mxHdPrimvarMap)[primvarName] = mxNodeDef->getType();
406             }
407         }
408 
409         // Connect the primvar node to the terminal node for HdStMaterialNetwork
410         // Create a unique name for the new connection.
411         std::string newConnName = primvarPath.GetName() + "_primvarconn";
412         HdMaterialConnection2 primvarConn;
413         primvarConn.upstreamNode = primvarPath;
414         primvarConn.upstreamOutputName = TfToken(newConnName);
415         hdNetwork->nodes[hdTerminalNodePath]
416             .inputConnections[primvarConn.upstreamOutputName] = {primvarConn};
417     }
418 }
419 
420 static std::string const&
_GetMaterialTag(HdMaterialNode2 const & terminal)421 _GetMaterialTag(HdMaterialNode2 const& terminal)
422 {
423     // Masked MaterialTag:
424     // UsdPreviewSurface: terminal.opacityThreshold value > 0
425     // StandardSurface materials do not have an opacityThreshold parameter
426     // so we StandardSurface will not use the Masked materialTag.
427     for (auto const& currParam : terminal.parameters) {
428         if (currParam.first != _tokens->opacityThreshold) continue;
429 
430         if (currParam.second.Get<float>() > 0.0f) {
431             return HdStMaterialTagTokens->masked.GetString();
432         }
433     }
434 
435     // Translucent MaterialTag
436     bool isTranslucent = false;
437 
438     // UsdPreviewSurface uses the opacity parameter to indicate the transparency
439     // when 1.0 the material is fully opaque, the smaller the value the more
440     // translucent the material, with a default value of 1.0
441     // StandardSurface indicates material transparency through two different
442     // parameters; the transmission parameter (float) where the greater the
443     // value the more transparent the material and a default value of 0.0,
444     // the opacity parameter (color3) indicating the opacity of the entire
445     // material, where the default value of (1,1,1) is fully opaque.
446 
447     // First check the opacity and transmission connections
448     auto const& opacityConnIt = terminal.inputConnections.find(_tokens->opacity);
449     if (opacityConnIt != terminal.inputConnections.end()) {
450         return HdStMaterialTagTokens->translucent.GetString();
451     }
452 
453     auto const& transmissionConnIt = terminal.inputConnections.find(
454                                                 _tokens->transmission);
455     isTranslucent = (transmissionConnIt != terminal.inputConnections.end());
456 
457     // Then check the opacity and transmission parameter value
458     if (!isTranslucent) {
459         for (auto const& currParam : terminal.parameters) {
460 
461             // UsdPreviewSurface
462             if (currParam.first == _tokens->opacity &&
463                 currParam.second.IsHolding<float>()) {
464                 isTranslucent = currParam.second.Get<float>() < 1.0f;
465                 break;
466             }
467             // StandardSurface
468             if (currParam.first == _tokens->opacity &&
469                 currParam.second.IsHolding<GfVec3f>()) {
470                 GfVec3f opacityColor = currParam.second.Get<GfVec3f>();
471                 isTranslucent |= ( opacityColor[0] < 1.0f
472                                 || opacityColor[1] < 1.0f
473                                 || opacityColor[2] < 1.0f );
474             }
475             if (currParam.first == _tokens->transmission &&
476                 currParam.second.IsHolding<float>()) {
477                 isTranslucent |= currParam.second.Get<float>() > 0.0f;
478             }
479         }
480     }
481 
482     if (isTranslucent) {
483         return HdStMaterialTagTokens->translucent.GetString();
484     }
485     return HdStMaterialTagTokens->defaultMaterialTag.GetString();
486 }
487 
488 
489 void
HdSt_ApplyMaterialXFilter(HdMaterialNetwork2 * hdNetwork,SdfPath const & materialPath,HdMaterialNode2 const & terminalNode,SdfPath const & terminalNodePath)490 HdSt_ApplyMaterialXFilter(
491     HdMaterialNetwork2* hdNetwork,
492     SdfPath const& materialPath,
493     HdMaterialNode2 const& terminalNode,
494     SdfPath const& terminalNodePath)
495 {
496     // Check if the Terminal is a MaterialX Node
497     SdrRegistry &sdrRegistry = SdrRegistry::GetInstance();
498     const SdrShaderNodeConstPtr mtlxSdrNode =
499         sdrRegistry.GetShaderNodeByIdentifierAndType(terminalNode.nodeTypeId,
500                                                      _tokens->mtlx);
501 
502     if (mtlxSdrNode) {
503 
504         // Load Standard Libraries/setup SearchPaths (for mxDoc and mxShaderGen)
505         mx::FilePathVec libraryFolders;
506         mx::FileSearchPath searchPath;
507         searchPath.append(mx::FilePath(PXR_MATERIALX_STDLIB_DIR));
508         mx::DocumentPtr stdLibraries = mx::createDocument();
509         mx::loadLibraries(libraryFolders, searchPath, stdLibraries);
510 
511         // Create the MaterialX Document from the HdMaterialNetwork
512         MxHdInfo mxHdInfo; // Hydra information for MaterialX glslfx shaderGen
513         std::set<SdfPath> hdTextureNodes;
514         std::set<SdfPath> hdPrimvarNodes;
515         mx::DocumentPtr mtlxDoc = HdMtlxCreateMtlxDocumentFromHdNetwork(
516                                         *hdNetwork,
517                                         terminalNode,   // MaterialX HdNode
518                                         materialPath,
519                                         stdLibraries,
520                                         &hdTextureNodes,
521                                         &mxHdInfo.textureMap,
522                                         &hdPrimvarNodes);
523 
524         // Add Hydra parameters for each of the Texture nodes
525         _UpdateTextureNodes(mtlxDoc, hdNetwork, terminalNodePath, hdTextureNodes,
526                             &mxHdInfo.textureMap, &mxHdInfo.primvarMap,
527                             &mxHdInfo.defaultTexcoordName);
528 
529         _UpdatePrimvarNodes(mtlxDoc, hdNetwork, terminalNodePath,
530                             hdPrimvarNodes, &mxHdInfo.primvarMap);
531 
532         mxHdInfo.materialTag = _GetMaterialTag(terminalNode);
533 
534         // Load MaterialX Document and generate the glslfxSource
535         std::string glslfxSource = HdSt_GenMaterialXShaderCode(mtlxDoc,
536                                     searchPath, mxHdInfo);
537 
538         // Create a new terminal node with the new glslfxSource
539         SdrShaderNodeConstPtr sdrNode =
540             sdrRegistry.GetShaderNodeFromSourceCode(glslfxSource,
541                                                     HioGlslfxTokens->glslfx,
542                                                     NdrTokenMap()); // metadata
543         HdMaterialNode2 newTerminalNode;
544         newTerminalNode.nodeTypeId = sdrNode->GetIdentifier();
545         newTerminalNode.inputConnections = terminalNode.inputConnections;
546         newTerminalNode.parameters = terminalNode.parameters;
547 
548         // Replace the original terminalNode with this newTerminalNode
549         hdNetwork->nodes[terminalNodePath] = newTerminalNode;
550     }
551 }
552 
553 PXR_NAMESPACE_CLOSE_SCOPE
554