2from pxr
import Usd, UsdShade, Sdf, UsdGeom, Gf
4def mapMtlxToUsdShaderNotation(name):
6 Utility to map from a MaterialX shader notation to Usd.
7 It would be easier if the same notation was used.
9 if name ==
'surfaceshader':
11 elif name ==
'displacementshader':
13 elif name ==
'volumshader':
17def emitUsdConnections(node, stage, rootPath):
19 Emit connections between MaterialX elements as Usd connections for
20 a given MaterialX node.
24 MaterialX node to examine
26 Usd stage to write connection to
32 if node.getType() ==
'material':
33 materialPath = node.getName()
35 for valueElement
in node.getActiveValueElements():
36 isInput = valueElement.isA(mx.Input)
37 isOutput = valueElement.isA(mx.Output)
38 if isInput
or isOutput:
44 mtlxConnection = valueElement.getAttribute(
'nodename')
45 if not mtlxConnection:
46 mtlxConnection = valueElement.getAttribute(
'nodegraph')
48 if not mtlxConnection:
49 mtlxConnection = valueElement.getAttribute(
'interfacename')
50 interfacename = mtlxConnection
61 parent = node.getParent()
62 if parent.getNamePath():
64 connectionPath = rootPath + parent.getNamePath()
66 connectionPath = rootPath + parent.getNamePath() +
'/' + mtlxConnection
71 connectionPath = rootPath
73 connectionPath = rootPath + mtlxConnection
77 parent = node.getParent()
80 if node.isA(mx.NodeGraph):
81 connectionPath = rootPath + node.getNamePath() +
'/' + mtlxConnection
84 if parent.getNamePath():
85 connectionPath = rootPath + parent.getNamePath() +
'/' + mtlxConnection
88 connectionPath = rootPath + mtlxConnection
92 connectionPath = connectionPath.removesuffix(
'/')
95 source = stage.GetPrimAtPath(connectionPath)
98 connectionPath =
'/' + materialPath + connectionPath
99 source = stage.GetPrimAtPath(connectionPath)
101 source = stage.GetPrimAtPath(
'/' + materialPath)
103 if source.IsA(UsdShade.Material):
104 sourcePrim = UsdShade.Material(source)
105 elif source.IsA(UsdShade.NodeGraph):
106 sourcePrim = UsdShade.NodeGraph(source)
107 elif source.IsA(UsdShade.Shader):
108 sourcePrim = UsdShade.Shader(source)
112 sourcePort = interfacename
114 sourcePort = valueElement.getAttribute(
'output')
118 mtlxConnection = mtlxConnection +
'. Port:' + sourcePort
121 print(
'> Failed to find source at path:', connectionPath)
127 dest = stage.GetPrimAtPath(rootPath + node.getNamePath())
129 print(
'> Failed to find dest at path:', rootPath + node.getNamePath())
132 portName = valueElement.getName()
134 if dest.IsA(UsdShade.Material):
135 destNode = UsdShade.Material(dest)
136 elif dest.IsA(UsdShade.NodeGraph):
137 destNode = UsdShade.NodeGraph(dest)
138 elif dest.IsA(UsdShade.Shader):
139 destNode = UsdShade.Shader(dest)
141 print(
'> Encountered unsupport destinion type')
147 if dest.IsA(UsdShade.Material):
148 portName = mapMtlxToUsdShaderNotation(portName)
149 portName =
'mtlx:' + portName
150 destPort = destNode.GetOutput(portName)
152 destPort = destNode.GetInput(portName)
154 destPort = destNode.GetOutput(portName)
159 interfaceInput = sourcePrim.GetInput(sourcePort)
161 if not destPort.ConnectToSource(interfaceInput):
162 print(
'> Failed to connect: ', source.GetPrimPath(),
'-->', destPort.GetFullName())
164 sourcePrimAPI = sourcePrim.ConnectableAPI()
165 if not destPort.ConnectToSource(sourcePrimAPI, sourcePort):
166 print(
'> Failed to connect: ', source.GetPrimPath(),
'-->', destPort.GetFullName())
168 print(
'> Failed to find destination port:', portName)
171def mapMtxToUsdType(mtlxType):
173 Map a MaterialX type to an Usd Sdf type
181 mtlxUsdMap[
'filename'] = Sdf.ValueTypeNames.Asset
182 mtlxUsdMap[
'string'] = Sdf.ValueTypeNames.String
183 mtlxUsdMap[
'boolean'] = Sdf.ValueTypeNames.Bool
184 mtlxUsdMap[
'integer'] = Sdf.ValueTypeNames.Int
185 mtlxUsdMap[
'float'] = Sdf.ValueTypeNames.Float
186 mtlxUsdMap[
'color3'] = Sdf.ValueTypeNames.Color3f
187 mtlxUsdMap[
'color4'] = Sdf.ValueTypeNames.Color4f
188 mtlxUsdMap[
'vector2'] = Sdf.ValueTypeNames.Float2
189 mtlxUsdMap[
'vector3'] = Sdf.ValueTypeNames.Vector3f
190 mtlxUsdMap[
'vector4'] = Sdf.ValueTypeNames.Float4
191 mtlxUsdMap[
'surfaceshader'] = Sdf.ValueTypeNames.Token
193 if mtlxType
in mtlxUsdMap:
194 return mtlxUsdMap[mtlxType]
195 return Sdf.ValueTypeNames.Token
197def mapMtxToUsdValue(mtlxType, mtlxValue):
199 Map a MaterialX value of a given type to a Usd value.
200 Note: Not all types are included here.
203 if mtlxType ==
'float':
205 elif mtlxType ==
'integer':
207 elif mtlxType ==
'boolean':
209 elif mtlxType ==
'string':
211 elif mtlxType ==
'filename':
213 elif mtlxType ==
'vector2':
214 usdValue = Gf.Vec2f( mtlxValue[0], mtlxValue[1] )
215 elif mtlxType ==
'color3' or mtlxType ==
'vector3':
216 usdValue = Gf.Vec3f( mtlxValue[0], mtlxValue[1], mtlxValue[2] )
217 elif mtlxType ==
'color4' or mtlxType ==
'vector4':
218 usdValue = Gf.Vec4f( mtlxValue[0], mtlxValue[1], mtlxValue[2], mtlxValue[3] )
222def emitUsdValueElements(node, usdNode, emitAllValueElements):
224 Emit MaterialX value elements in Usd.
229 MaterialX node with value elements to scan
231 UsdShade node to create value elements on.
232 emitAllValueElements: bool
233 Emit value elements based on node definition, even if not specified on node instance.
238 isMaterial = node.getType() ==
'material'
242 nodedef = node.getNodeDef()
243 if nodedef
and not isMaterial:
244 for valueElement
in nodedef.getActiveValueElements():
245 if valueElement.isA(mx.Input):
246 if emitAllValueElements:
247 mtlxType = valueElement.getType()
248 usdType = mapMtxToUsdType(mtlxType)
250 portName = valueElement.getName()
251 usdInput = usdNode.CreateInput(portName, usdType)
253 if len(valueElement.getValueString()) > 0:
254 mtlxValue = valueElement.getValue()
255 usdValue = mapMtxToUsdValue(mtlxType, mtlxValue)
257 usdInput.Set(usdValue)
259 elif not isMaterial
and valueElement.isA(mx.Output):
260 usdOutput = usdNode.CreateOutput(valueElement.getName(), mapMtxToUsdType(valueElement.getType()))
263 print(
'- Skip mapping of definition element: ', valueElement.getName(),
'. Type: ', valueElement.getCategory())
267 for valueElement
in node.getActiveValueElements():
268 if valueElement.isA(mx.Input):
269 mtlxType = valueElement.getType()
270 usdType = mapMtxToUsdType(mtlxType)
271 portName = valueElement.getName()
274 portName = mapMtlxToUsdShaderNotation(portName)
275 usdInput = usdNode.CreateOutput(
'mtlx:' + portName, usdType)
277 usdInput = usdNode.CreateInput(portName, usdType)
281 if len(valueElement.getValueString()) > 0:
282 mtlxValue = valueElement.getValue()
283 usdValue = mapMtxToUsdValue(mtlxType, mtlxValue)
285 usdInput.Set(usdValue)
287 elif not isMaterial
and valueElement.isA(mx.Output):
288 usdOutput = usdNode.GetInput(valueElement.getName())
290 usdOutput = usdNode.CreateOutput(valueElement.getName(), mapMtxToUsdType(valueElement.getType()))
293 print(
'- Skip mapping of element: ', valueElement.getNamePath(),
'. Type: ', valueElement.getCategory())
296def moveChild(newParent, child):
297 newChild = newParent.addChildOfCategory(child.getCategory(), child.getName())
298 print(newChild.getNamePath())
299 newChild.copyContentFrom(child)
300 oldParent = child.getParent()
301 oldParent.removeChild(child.getName())
303def emitUsdShaderGraph(doc, stage, mxnodes, emitAllValueElements):
305 Emit Usd shader graph to a given stage from a list of MaterialX nodes.
310 MaterialX source document
314 MaterialX shader nodes.
315 emitAllValueElements: bool
316 Emit value elements based on node definition, even if not specified on node instance.
319 print(
'Stage:', stage)
324 elem = doc.getDescendant(v)
325 if elem.getType() ==
'material':
326 materialPath = elem.getName()
331 elem = doc.getDescendant(v)
335 usdPath =
'/' + elem.getNamePath()
339 if elem.getType() ==
'material':
340 usdNode = UsdShade.Material.Define(stage, usdPath)
341 elif elem.isA(mx.Node):
342 nodeDef = elem.getNodeDef()
344 elemPath =
'/' + materialPath + usdPath
347 usdNode = UsdShade.Shader.Define(stage, elemPath)
348 elif elem.isA(mx.NodeGraph):
350 elemPath =
'/' + materialPath + usdPath
353 usdNode = UsdShade.NodeGraph.Define(stage, elemPath)
357 usdNode.SetShaderId(nodeDef.getName())
358 emitUsdValueElements(elem, usdNode, emitAllValueElements)
362 elem = doc.getDescendant(v)
363 usdPath =
'/' + elem.getNamePath()
365 if elem.getType() ==
'material':
366 emitUsdConnections(elem, stage,
'/')
367 elif elem.isA(mx.Node):
369 emitUsdConnections(elem, stage,
'/' + materialPath +
'/')
370 elif elem.isA(mx.NodeGraph):
372 emitUsdConnections(elem, stage,
'/' + materialPath +
'/')
374def findMaterialXNodes(doc):
376 Find all nodes in a MaterialX document
379 treeIter = doc.traverseTree()
380 for elem
in treeIter:
381 path = elem.getNamePath()
382 if path
in visitedNodes:
384 visitedNodes.append(path)
387def convertMtlxToUsd(doc, emitAllValueElements):
389 Read in a MaterialX file and emit it to a new Usd Stage
390 Dump results for display and save to usda file.
394 mtlxFileName : string
395 Name of file containing MaterialX document. Assumed to end in ".mtlx"
396 emitAllValueElements: bool
397 Emit value elements based on node definition, even if not specified on node instance.
399 stage = Usd.Stage.CreateInMemory()
409 mxnodes = findMaterialXNodes(doc)
410 stdlib = mx.createDocument()
412 searchPath = mx.getDefaultDataSearchPath()
413 libFiles = mx.loadLibraries(mx.getDefaultDataLibraryFolders(), searchPath, stdlib)
414 doc.importLibrary(stdlib)
417 emitUsdShaderGraph(doc, stage, mxnodes, emitAllValueElements)
424 stageString = stage.GetRootLayer().ExportToString()