146 @brief Add downstream materials to the MaterialX graph.
147 @param doc The MaterialX document.
148 @return The number of materials added.
151 material_count = len(doc.getMaterialNodes())
152 if material_count > 0:
155 nodegraphs = doc.getNodeGraphs()
160 supported_output_types = [
'float',
'vector2',
'vector3',
'vector4',
'integer',
'boolean',
'color3',
'color4' ]
162 for graph
in nodegraphs:
163 if graph.hasSourceUri():
167 graph_outputs = graph.getOutputs()
168 if not graph_outputs:
171 self.
logger.debug(f
'Scan graph: {graph.getName()}')
174 usd_supports_convert_to_surface_shader =
False
176 downstream_ports = graph.getDownstreamPorts()
177 for output
in graph_outputs:
180 for port
in downstream_ports:
181 if port.getNamePath() == output.getNamePath():
185 downstream_ports.remove(port)
198 self.
logger.info(
'>>> Downstream port:' +
",".join( [port.getNamePath()
for port
in downstream_ports]))
199 if len(downstream_ports) == 0:
202 is_multi_output = len(graph_outputs) > 1
203 for output
in graph_outputs:
210 output_name = output.getName()
211 output_type = output.getType()
216 if output_type ==
'surfaceshader':
217 graph_parent = graph.getParent()
218 connected_ss = output.getConnectedNode()
219 if not graph_parent
or not connected_ss:
222 self.
logger.info(f
'Extract unsupported shader inside nodegraph: {connected_ss.getNamePath()}')
224 shadernode_name = graph_parent.createValidChildName(connected_ss.getName())
225 shadernode_nodedef = connected_ss.getNodeDef()
226 shadernode = graph_parent.addNodeInstance(shadernode_nodedef, shadernode_name)
227 shadernode.copyContentFrom(connected_ss)
231 for ss_input
in connected_ss.getInputs():
232 ss_input_input = ss_input.getNodeName()
if ss_input.getNodeName()
else ss_input.getInterfaceName()
233 if not ss_input_input:
235 ss_input_type = ss_input.getType()
236 ss_input_output = graph.addOutput(graph.createValidChildName(
'out'), ss_input_type)
237 ss_input_output.setNodeName(ss_input_input)
238 if ss_input.getOutputString():
239 ss_input_output.setOutputString(ss_input.getOutputString())
242 shadernode_input = shadernode.getInput(ss_input.getName())
244 shadernode_input.removeAttribute(
'nodename')
245 shadernode_input.removeAttribute(
'value')
246 shadernode_input.setNodeGraphString(graph.getName())
247 shadernode_input.setOutputString(ss_input_output.getName())
250 graph.removeNode(connected_ss.getName())
251 graph.removeOutput(output_name)
254 material_name = doc.createValidChildName(graph.getName() +
'_' + output_name)
255 material_node = doc.addMaterialNode(material_name)
257 self.
logger.info(f
"Added material node: {material_node.getName()}, for graph shader output: {output_name}")
258 material_node_input = material_node.addInput(output_type, output_type)
261 material_node_input.setNodeName(shadernode_name)
262 material_node_input.removeAttribute(
'value')
265 elif output_type
in supported_output_types:
267 if usd_supports_convert_to_surface_shader:
269 shadernode_name = doc.createValidChildName(
'SHD_' + graph.getName() +
'_' + output_name)
270 materialnode_name = doc.createValidChildName(
'MAT' + graph.getName() +
'_' + output_name)
272 convert_definition =
'ND_convert_' + output_type +
'_color3'
273 convert_node = doc.getNodeDef(convert_definition)
275 self.
logger.info(f
'>>>> Failed to find conversion definition: {convert_definition}')
277 shadernode = doc.addNodeInstance(convert_node, shadernode_name)
278 shadernode.removeAttribute(
'nodedef')
279 new_input = shadernode.addInput(
'in', output_type)
280 new_input.setNodeGraphString(graph.getName())
281 new_input.removeAttribute(
'value')
285 new_input.setOutputString(output_name)
286 materialnode = doc.addMaterialNode(materialnode_name, shadernode)
296 if output_type !=
'color3' and output_type !=
'float':
298 convert_definition =
'ND_convert_' + output_type +
'_color3'
299 convert_nodedef = doc.getNodeDef(convert_definition)
300 if not convert_nodedef:
301 self.
logger.info(
">>>> Failed to find conversion definition: %s" % convert_definition)
305 convert_upstream =
None
306 if len(output.getNodeName()) > 0:
307 convert_upstream = output.getNodeName()
308 elif output.hasInterfaceName():
309 convert_upstream = output.getInterfaceName()
310 if not convert_upstream:
311 self.
logger.info(
"> Failed to find upstream node for output: %s" % output.getName())
315 convert_node = graph.addNodeInstance(convert_nodedef, graph.createValidChildName(f
'convert_{convert_upstream}'))
316 convert_node.removeAttribute(
'nodedef')
317 convert_input = convert_node.addInput(
'in', output_type)
318 if len(output.getNodeName()) > 0:
319 convert_input.setNodeName(output.getNodeName())
320 elif output.hasInterfaceName():
321 convert_input.setInterfaceName(output.getInterfaceName())
322 convert_input.removeAttribute(
'value')
326 output.setNodeName(convert_node.getName())
327 output.removeAttribute(
'value')
328 output.setType(
'color3')
329 output_type =
'color3'
332 shadernode_name = doc.createValidChildName(
'shader_' + graph.getName() +
'_' + output_name)
333 materialnode_name = doc.createValidChildName(graph.getName() +
'_' + output_name)
334 unlitDefinition =
'ND_surface_unlit'
335 unlitNode = doc.getNodeDef(unlitDefinition)
336 shadernode = doc.addNodeInstance(unlitNode, shadernode_name)
337 shadernode.removeAttribute(
'nodedef')
341 if output_type ==
'color3':
342 new_input = shadernode.addInput(
'emission_color', output_type)
344 new_input = shadernode.addInput(
'emission', output_type)
345 new_input.setNodeGraphString(graph.getName())
346 new_input.removeAttribute(
'value')
350 new_input.setOutputString(output_name)
353 materialnode = doc.addMaterialNode(materialnode_name, shadernode)
358 return material_count
363 @brief Add explicit geometry stream nodes for inputs with defaultgeomprop specified
364 in nodes definition. Do this for unconnected inputs only.
365 @param graph The MaterialX graph element.
366 @return The number of implicit nodes added.
369 graph_default_nodes = {}
371 for node
in graph.getNodes():
372 if node.hasSourceUri()
or (node.getCategory()
in [
"nodedef"]):
375 nodedef = node.getNodeDef(node.getType())
380 for nodedef_input
in nodedef.getInputs():
381 node_input = node.getInput(nodedef_input.getName())
384 if node_input.getInterfaceName()
or node_input.getNodeName()
or node_input.getNodeGraphString():
388 defaultgeomprop = nodedef_input.getDefaultGeomProp()
389 if not defaultgeomprop:
394 if defaultgeomprop.getGeomProp() ==
"bitangent":
400 defaultgeomprop_name = defaultgeomprop.getName()
401 defaultgeomprop_prop = defaultgeomprop.getGeomProp()
402 defaultgeomprop_type = defaultgeomprop.getType()
403 defaultgeomprop_index = defaultgeomprop.getIndex()
404 defaultgeomprop_space = defaultgeomprop.getSpace()
407 node_input = node.addInput(nodedef_input.getName(), nodedef_input.getType())
408 if defaultgeomprop_name
not in graph_default_nodes:
409 upstream_default_node = graph.addNode(defaultgeomprop_prop,
410 graph.createValidChildName(defaultgeomprop_name),
411 defaultgeomprop_type)
412 upstream_default_node.addInputsFromNodeDef()
415 index_input = upstream_default_node.getInput(
"index")
417 index_input.setValue(defaultgeomprop_index,
'integer')
418 space_input = upstream_default_node.getInput(
"space")
420 space_input.setValue(defaultgeomprop_space,
'string')
423 graph_default_nodes[defaultgeomprop_name] = upstream_default_node
425 upstream_default_node = graph_default_nodes[defaultgeomprop_name]
427 node_input.setNodeName(upstream_default_node.getName())
428 node_input.removeAttribute(
'value')
430 implicit_nodes_added = len(graph_default_nodes)
431 if graph.getCategory()
not in "nodegraph":
432 for child_graph
in graph.getNodeGraphs():
433 if child_graph.hasSourceUri():
437 return implicit_nodes_added
442 @brief Encapsulate top level nodes in a nodegraph. Remap any connections to the top level nodes
443 to outputs on a new nodegraph.
444 @param doc The MaterialX document.
445 @param nodegraph_name The name of the new nodegraph to encapsulate the top level nodes. Default is 'top_level_nodes'.
446 @param remove_original If True, remove the original top level nodes from the document. Default is True.
447 @return The number of top level nodes found
450 top_level_nodes_found = 0
454 top_level_connections = []
455 for elem
in doc.getNodes():
458 if elem.hasSourceUri():
462 and (elem.getType()
not in [
"material",
"surfaceshader"])
463 and elem.getCategory()
not in [
"nodegraph",
"nodedef"]):
465 top_level_nodes.append(elem)
467 elif elem.getType()
in [
"surfaceshader"]:
468 for input_port
in elem.getInputs():
469 upstream_node_name = input_port.getNodeName()
470 if len(upstream_node_name) > 0:
471 upstream_output_name = input_port.getOutputString()
477 if not upstream_output_name:
478 upstream_node = doc.getDescendant(upstream_node_name)
479 upstream_node_outputs = upstream_node.getOutputs()
480 if len(upstream_node_outputs) > 1:
481 self.
logger.debug(f
"Find an output of name: {upstream_node_outputs[0].getName()}")
482 upstream_output_name = upstream_node_outputs[0].getName()
484 upstream_node_nodedef = upstream_node.getNodeDef()
485 upstream_node_outputs = upstream_node_nodedef.getActiveOutputs()
486 if len(upstream_node_outputs) > 1:
487 self.
logger.debug(f
"Find an output of name: {upstream_node_outputs[0].getName()}")
488 upstream_output_name = upstream_node_outputs[0].getName()
491 top_level_connections.append([upstream_node_name, input_port.getNamePath(), upstream_output_name])
494 top_level_nodes_found = len(top_level_nodes)
495 if top_level_nodes_found == 0:
496 return top_level_nodes_found
499 ng_name = doc.createValidChildName(nodegraph_name)
500 ng = doc.addNodeGraph(ng_name)
501 for node
in top_level_nodes:
503 new_node = ng.addNode(node.getCategory(), mx.createValidName(node.getName()), node.getType())
504 new_node.copyContentFrom(node)
505 for connect
in top_level_connections:
506 if connect[0] == node.getName():
507 the_input = doc.getDescendant(connect[1])
512 new_output = ng.addOutput(ng.createValidChildName(
"out"), the_input.getType())
513 new_output.setNodeName(new_node.getName())
514 if len(connect[2]) > 0:
515 new_output.setOutputString(connect[2])
517 the_input.setNodeGraphString(ng_name)
518 the_input.setOutputString(new_output.getName())
519 the_input.removeAttribute(
"nodename")
520 the_input.removeAttribute(
"value")
522 connections_made += 1
525 for node
in top_level_nodes:
526 doc.removeChild(node.getName())
528 return top_level_nodes_found