51 Map a MaterialX type to a USD Sdf type.The mapping is easier from MaterialX as
52 the number of type variations is much less. Note that one USD type is chosen
53 with no options for choosing things like precision.
54 @param mtlx_type: MaterialX type.
55 @return: Corresponding USD Sdf type.
58 "filename": Sdf.ValueTypeNames.Asset,
59 "string": Sdf.ValueTypeNames.String,
60 "boolean": Sdf.ValueTypeNames.Bool,
61 "integer": Sdf.ValueTypeNames.Int,
62 "float": Sdf.ValueTypeNames.Float,
63 "color3": Sdf.ValueTypeNames.Color3f,
64 "color4": Sdf.ValueTypeNames.Color4f,
65 "vector2": Sdf.ValueTypeNames.Float2,
66 "vector3": Sdf.ValueTypeNames.Float3,
67 "vector4": Sdf.ValueTypeNames.Float4,
68 "surfaceshader": Sdf.ValueTypeNames.Token,
69 "volumeshader": Sdf.ValueTypeNames.Token,
70 "displacementshader": Sdf.ValueTypeNames.Token,
72 return mtlx_usd_map.get(mtlx_type, Sdf.ValueTypeNames.Token)
76 Map a MaterialX value of a given type to a USD value.
77 TODO: Add all types here. This does not seem to be exposed in Python?
78 See: https://openusd.org/dev/api/struct_usd_mtlx_usd_type_info.html
79 @param mtlx_type: MaterialX type.
80 @param mtlx_value: MaterialX value.
81 @return: Corresponding USD value.
83 if mtlx_type ==
"float":
84 return float(mtlx_value)
85 elif mtlx_type ==
"integer":
86 return int(mtlx_value)
87 elif mtlx_type ==
"boolean":
88 return bool(mtlx_value)
89 elif mtlx_type
in (
"string",
"filename"):
90 return str(mtlx_value)
91 elif mtlx_type ==
"vector2":
92 return Gf.Vec2f(mtlx_value[0], mtlx_value[1])
93 elif mtlx_type
in (
"color3",
"vector3"):
94 return Gf.Vec3f(mtlx_value[0], mtlx_value[1], mtlx_value[2])
95 elif mtlx_type
in (
"color4",
"vector4"):
96 return Gf.Vec4f(mtlx_value[0], mtlx_value[1], mtlx_value[2], mtlx_value[3])
97 elif mtlx_type ==
"matraix33":
98 return Gf.Matrix3f(mtlx_value[0], mtlx_value[1], mtlx_value[2],
99 mtlx_value[3], mtlx_value[4], mtlx_value[5],
100 mtlx_value[6], mtlx_value[7], mtlx_value[8])
101 elif mtlx_type ==
"matrix44":
102 return Gf.Matrix4f(mtlx_value[0], mtlx_value[1], mtlx_value[2], mtlx_value[3],
103 mtlx_value[4], mtlx_value[5], mtlx_value[6], mtlx_value[7],
104 mtlx_value[8], mtlx_value[9], mtlx_value[10], mtlx_value[11],
105 mtlx_value[12], mtlx_value[13], mtlx_value[14], mtlx_value[15])
124 Emit connections between MaterialX elements as USD connections for
125 a given MaterialX node.
126 @param node: MaterialX node to examine.
127 @param stage: USD stage to write connection to.
128 @param root_path: Root path for connections.
134 if node.getType() ==
"material":
135 material_path = node.getName()
137 value_elements = node.getActiveValueElements()
if (node.isA(mx.Node)
or node.isA(mx.NodeGraph))
else [ node ]
138 for value_element
in value_elements:
139 is_input = value_element.isA(mx.Input)
140 is_output = value_element.isA(mx.Output)
142 if is_input
or is_output:
147 mtlx_connection = value_element.getAttribute(
"nodename")
148 if not mtlx_connection:
149 mtlx_connection = value_element.getAttribute(
"nodegraph")
150 if is_input
and not mtlx_connection:
151 mtlx_connection = value_element.getAttribute(
"interfacename")
152 interface_name = mtlx_connection
162 parent = node.getParent()
163 if parent.getNamePath():
165 connection_path = root_path + parent.getNamePath()
167 connection_path = root_path + parent.getNamePath() +
"/" + mtlx_connection
172 connection_path = root_path
174 connection_path = root_path + mtlx_connection
178 parent = node.getParent()
181 if node.isA(mx.NodeGraph):
182 connection_path = root_path + node.getNamePath() +
"/" + mtlx_connection
185 if parent.getNamePath():
186 connection_path = root_path + parent.getNamePath() +
"/" + mtlx_connection
189 connection_path = root_path + mtlx_connection
193 connection_path = connection_path.removesuffix(
"/")
196 source = stage.GetPrimAtPath(connection_path)
197 if not source
and material_path:
198 connection_path =
"/" + material_path + connection_path
199 source = stage.GetPrimAtPath(connection_path)
201 source = stage.GetPrimAtPath(
"/" + material_path)
204 if source.IsA(UsdShade.Material):
205 source_prim = UsdShade.Material(source)
206 elif source.IsA(UsdShade.NodeGraph):
207 source_prim = UsdShade.NodeGraph(source)
208 elif source.IsA(UsdShade.Shader):
209 source_prim = UsdShade.Shader(source)
213 source_port = interface_name
215 source_port = value_element.getAttribute(
"output")
or "out"
220 dest = stage.GetPrimAtPath(root_path + node.getNamePath())
222 port_name = value_element.getName()
224 if dest.IsA(UsdShade.Material):
225 dest_node = UsdShade.Material(dest)
226 elif dest.IsA(UsdShade.NodeGraph):
227 dest_node = UsdShade.NodeGraph(dest)
228 elif dest.IsA(UsdShade.Shader):
229 dest_node = UsdShade.Shader(dest)
235 if dest.IsA(UsdShade.Material):
237 port_name =
"mtlx:" + port_name
238 dest_port = dest_node.GetOutput(port_name)
240 dest_port = dest_node.GetInput(port_name)
242 dest_port = dest_node.GetOutput(port_name)
247 interface_input = source_prim.GetInput(source_port)
249 if not dest_port.ConnectToSource(interface_input):
250 self.
log(f
"> Failed to connect: {source.GetPrimPath()} --> {dest_port.GetFullName()}")
252 source_prim_api = source_prim.ConnectableAPI()
253 if not dest_port.ConnectToSource(source_prim_api, source_port):
254 self.
log(f
"> Failed to connect: {source.GetPrimPath()} --> {dest_port.GetFullName()}")
256 self.
log(f
"> Failed to find destination port: {port_name}")
260 Emit MaterialX value elements in USD.
261 @param node: MaterialX node with value elements to scan.
262 @param usd_node: UsdShade node to create value elements on.
263 @param emit_all_value_elements: Emit value elements based on node definition, even if not specified on node instance.
268 is_material = node.getType() ==
"material"
270 if node.isA(mx.Node):
271 node_def = node.getNodeDef()
275 if node_def
and not is_material:
276 for value_element
in node_def.getActiveValueElements():
277 if value_element.isA(mx.Input):
278 if emit_all_value_elements:
279 mtlx_type = value_element.getType()
281 port_name = value_element.getName()
282 usd_input = usd_node.CreateInput(port_name, usd_type)
284 if value_element.getValueString():
285 mtlx_value = value_element.getValue()
287 if usd_value
is not None:
288 usd_input.Set(usd_value)
289 color_space = value_element.getAttribute(
"colorspace")
291 usd_input.GetAttr().SetColorSpace(color_space)
292 uifolder = value_element.getAttribute(
"uifolder")
294 usd_input.SetDisplayGroup(uifolder)
295 uiname = value_element.getAttribute(
"uiname")
297 usd_input.GetAttr().SetDisplayName(uiname)
299 elif not is_material
and value_element.isA(mx.Output):
300 usd_node.CreateOutput(value_element.getName(), self.
map_mtlx_to_usd_type(value_element.getType()))
305 if node.isA(mx.Node)
or node.isA(mx.NodeGraph):
306 value_elements = node.getActiveValueElements()
308 value_elements = [ node ]
309 for value_element
in value_elements:
310 if value_element.isA(mx.Input):
311 mtlx_type = value_element.getType()
313 port_name = value_element.getName()
317 usd_input = usd_node.CreateOutput(
"mtlx:" + port_name, usd_type)
319 usd_input = usd_node.CreateInput(port_name, usd_type)
323 if value_element.getValueString():
324 mtlx_value = value_element.getValue()
326 if usd_value
is not None:
327 usd_input.Set(usd_value)
328 color_space = value_element.getAttribute(
"colorspace")
330 usd_input.GetAttr().SetColorSpace(color_space)
331 uifolder = value_element.getAttribute(
"uifolder")
333 usd_input.SetDisplayGroup(uifolder)
334 uiname = value_element.getAttribute(
"uiname")
336 usd_input.GetAttr().SetDisplayName(uiname)
338 elif not is_material
and value_element.isA(mx.Output):
339 usd_output = usd_node.GetInput(value_element.getName())
341 usd_node.CreateOutput(value_element.getName(), self.
map_mtlx_to_usd_type(value_element.getType()))
365 Emit USD shader graph to a given stage from a list of MaterialX nodes.
366 @param doc: MaterialX source document.
367 @param stage: USD target stage.
368 @param mtlx_nodes: MaterialX shader nodes.
369 @param emit_all_value_elements: Emit value elements based on node definition, even if not specified on node instance.
370 @param root: Root path for the shader graph.
372 mtx_version = doc.getVersionString()
375 declare_version_at_root =
False
376 if declare_version_at_root:
377 root_prim = stage.DefinePrim(
"/MaterialX/Materials")
381 for node_name
in mtlx_nodes:
382 elem = doc.getDescendant(node_name)
383 if elem.getType() ==
"material":
384 material_path = elem.getName()
388 for node_name
in mtlx_nodes:
389 elem = doc.getDescendant(node_name)
390 usd_path = root + elem.getNamePath()
394 if elem.getType() ==
"material":
395 self.
log(f
"Add material at path: {usd_path}", -1)
397 usd_node = UsdShade.Material.Define(stage, usd_path)
398 material_prim = usd_node.GetPrim()
399 if not declare_version_at_root:
401 material_prim.ApplyAPI(
"MaterialXConfigAPI")
402 elif elem.isA(mx.Node):
403 node_def = elem.getNodeDef()
404 self.
log(f
"Add node at path: {usd_path}", -1)
405 usd_node = UsdShade.Shader.Define(stage, usd_path)
406 if not declare_version_at_root:
408 elif elem.isA(mx.NodeGraph):
409 self.
log(f
"Add nodegraph at path: {usd_path}", -1)
410 usd_node = UsdShade.NodeGraph.Define(stage, usd_path)
414 usd_node.SetShaderId(node_def.getName())
418 for node_name
in mtlx_nodes:
419 elem = doc.getDescendant(node_name)
420 if elem.getType() ==
"material" or elem.isA(mx.Node)
or elem.isA(mx.NodeGraph):
457 def emit(self, mtlx_file_name, emit_all_value_elements):
459 Read in a MaterialX file and emit it to a new USD Stage.
460 Dump results for display and save to usda file.
461 @param mtlx_file_name: Name of file containing MaterialX document. Assumed to end in ".mtlx".
462 @param emit_all_value_elements: Emit value elements based on node definition, even if not specified on node instance.
465 stage = Usd.Stage.CreateInMemory()
466 doc = mx.createDocument()
467 mtlx_file_path = mx.FilePath(mtlx_file_name)
469 if not mtlx_file_path.exists():
470 self.
log(f
"Failed to read file: {mtlx_file_path.asString()}")
474 mx.readFromXmlFile(doc, mtlx_file_name)
478 doc.setDataLibrary(stdlib)