360 Export a MaterialX nodegraph to a glTF procedural graph.
361 @param graph: The MaterialX nodegraph to export.
362 @param json: The JSON object to export the procedural graph to.
363 meaning to export all materials.
364 @return The procedural graph JSON object if successful, otherwise None.
366 no_result = [
None,
None,
None]
368 graph_outputs = graph.getOutputs()
369 if len(graph_outputs) == 0:
370 self.
logger.info(f
'> No graph outputs found on graph: {graph.getNamePath()}')
376 images_block = json.get(KHR_IMAGES_BLOCK, [])
377 if KHR_IMAGES_BLOCK
not in json:
378 json[KHR_IMAGES_BLOCK] = images_block
380 texture_array = json.get(KHR_TEXTURES_BLOCK, [])
381 if KHR_TEXTURES_BLOCK
not in json:
382 json[KHR_TEXTURES_BLOCK] = texture_array
387 nodegraph_inputs = {}
388 nodegraph_outputs = {}
391 extensions = json.get(KHR_EXTENSIONS_BLOCK, {})
392 if KHR_EXTENSIONS_BLOCK
not in json:
393 json[KHR_EXTENSIONS_BLOCK] = extensions
395 KHR_texture_procedurals = extensions.get(KHR_TEXTURE_PROCEDURALS, {})
396 if KHR_TEXTURE_PROCEDURALS
not in extensions:
397 extensions[KHR_TEXTURE_PROCEDURALS] = KHR_texture_procedurals
399 if KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK
not in KHR_texture_procedurals:
400 KHR_texture_procedurals[KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK] = []
402 procs = KHR_texture_procedurals[KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK]
404 'name': graph.getNamePath()
if use_paths
else graph.getName(),
405 'nodetype': graph.getCategory()
408 nodegraph[KHR_TEXTURE_PROCEDURALS_TYPE] = MULTI_OUTPUT_TYPE_STRING
if len(graph_outputs) > 1
else graph_outputs[0].getType()
409 nodegraph[KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK] = []
410 nodegraph[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK] = []
411 nodegraph[KHR_TEXTURE_PROCEDURALS_NODES_BLOCK] = []
412 procs.append(nodegraph)
418 for meta
in graph_metadata:
419 if graph.getAttribute(meta):
420 nodegraph[meta] = graph.getAttribute(meta)
424 for node
in graph.getNodes():
425 json_node = {
'name': node.getNamePath()
if use_paths
else node.getName()}
426 nodegraph[KHR_TEXTURE_PROCEDURALS_NODES_BLOCK].append(json_node)
427 nodegraph_nodes[node.getNamePath()] = len(nodegraph[KHR_TEXTURE_PROCEDURALS_NODES_BLOCK]) - 1
431 for input
in graph.getInputs():
433 'name': input.getNamePath()
if use_paths
else input.getName(),
434 'nodetype': input.getCategory()
437 for meta
in metadata:
438 if input.getAttribute(meta):
439 json_node[meta] = input.getAttribute(meta)
442 if input.getValue()
is not None:
443 input_type = input.getAttribute(mx.TypedElement.TYPE_ATTRIBUTE)
444 json_node[KHR_TEXTURE_PROCEDURALS_TYPE] = input_type
445 if input_type == mx.FILENAME_TYPE_STRING:
447 filename = input.getResolvedValueString()
450 texture_array.append(texture)
451 json_node[KHR_TEXTURE_PROCEDURALS_TEXTURE] = len(texture_array) - 1
453 value = input.getValueString()
455 json_node[KHR_TEXTURE_PROCEDURALS_VALUE] = value
456 nodegraph[KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK].append(json_node)
459 nodegraph_inputs[input.getNamePath()] = len(nodegraph[KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK]) - 1
461 self.
logger.error(
'> No value or invalid connection specified for input. Input skipped:', input.getNamePath())
465 for output
in graph_outputs:
466 json_node = {KHR_TEXTURE_PROCEDURALS_NAME: output.getNamePath()
if use_paths
else output.getName()}
467 nodegraph[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK].append(json_node)
468 nodegraph_outputs[output.getNamePath()] = len(nodegraph[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK]) - 1
470 json_node[KHR_TEXTURE_PROCEDURALS_NODETYPE] = output.getCategory()
471 json_node[KHR_TEXTURE_PROCEDURALS_TYPE] = output.getType()
474 for meta
in metadata:
475 if output.getAttribute(meta):
476 json_node[meta] = output.getAttribute(meta)
480 connection = output.getAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE)
481 if len(connection) == 0:
482 connection = output.getAttribute(MTLX_NODE_NAME_ATTRIBUTE)
484 connection_node = graph.getChild(connection)
486 connection_path = connection_node.getNamePath()
488 json_node[
'debug_connection_path'] = connection_path
491 if nodegraph_inputs.get(connection_path)
is not None:
492 json_node[KHR_TEXTURE_PROCEDURALS_INPUT] = nodegraph_inputs[connection_path]
493 elif nodegraph_nodes.get(connection_path)
is not None:
494 json_node[KHR_TEXTURE_PROCEDURALS_NODE] = nodegraph_nodes[connection_path]
496 self.
logger.error(f
'> Invalid output connection to: {connection_path}')
499 output_string = output.getAttribute(MTLX_OUTPUT_ATTRIBUTE)
500 if len(output_string) > 0:
501 json_node[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_string
504 for node
in graph.getNodes():
505 json_node = nodegraph[KHR_TEXTURE_PROCEDURALS_NODES_BLOCK][nodegraph_nodes[node.getNamePath()]]
506 json_node[KHR_TEXTURE_PROCEDURALS_NODETYPE] = node.getCategory()
507 nodedef = node.getNodeDef()
511 self.
logger.error(f
'> Missing nodedef for node: {node.getNamePath()}')
514 if debug
and nodedef
and nodedef.getNodeGroup():
515 json_node[KHR_TEXTURE_PROCEDURALS_NODEGROUP] = nodedef.getNodeGroup()
517 for attr_name
in node.getAttributeNames():
518 json_node[attr_name] = node.getAttribute(attr_name)
523 for input
in node.getInputs():
525 'name': input.getName(),
529 for meta
in metadata:
530 if input.getAttribute(meta):
531 input_item[meta] = input.getAttribute(meta)
533 input_type = input.getAttribute(mx.TypedElement.TYPE_ATTRIBUTE)
534 input_item[KHR_TEXTURE_PROCEDURALS_TYPE] = input_type
539 connection = input.getAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE)
542 connection = input.getAttribute(MTLX_NODE_NAME_ATTRIBUTE)
545 connection_node = graph.getChild(connection)
547 connection_path = connection_node.getNamePath()
549 input_item[
'debug_connection_path'] = connection_path
551 if is_interface
and nodegraph_inputs.get(connection_path)
is not None:
552 input_item[KHR_TEXTURE_PROCEDURALS_INPUT] = nodegraph_inputs[connection_path]
553 elif nodegraph_nodes.get(connection_path)
is not None:
554 input_item[KHR_TEXTURE_PROCEDURALS_NODE] = nodegraph_nodes[connection_path]
556 output_string = input.getAttribute(MTLX_OUTPUT_ATTRIBUTE)
558 connected_node_outputs = connection_node.getOutputs()
559 for i, connected_output
in enumerate(connected_node_outputs):
560 if connected_output.getName() == output_string:
561 input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT] = i
564 self.
logger.error(f
'> Invalid input connection to: '
565 '{connection} from input: {input.getNamePath()} '
566 'node: {node.getNamePath()}')
569 elif input.getValue()
is not None:
570 if input_type == mx.FILENAME_TYPE_STRING:
572 filename = input.getResolvedValueString()
574 texture_array.append(texture)
575 input_item[KHR_TEXTURE_PROCEDURALS_TEXTURE] = len(texture_array) - 1
577 value = input.getValueString()
579 input_item[KHR_TEXTURE_PROCEDURALS_VALUE] = value
581 inputs.append(input_item)
585 json_node[KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK] = inputs
589 for output
in node.getOutputs():
591 'nodetype': KHR_TEXTURE_PROCEDURALS_OUTPUT,
592 'name': output.getName(),
593 KHR_TEXTURE_PROCEDURALS_TYPE: output.getType()
595 outputs.append(output_item)
599 for output
in nodedef.getOutputs():
600 if not any(output_item[KHR_TEXTURE_PROCEDURALS_NAME] == output.getName()
for output_item
in outputs):
602 'nodetype': KHR_TEXTURE_PROCEDURALS_OUTPUT,
603 'name': output.getName(),
604 KHR_TEXTURE_PROCEDURALS_TYPE: output.getType()
606 outputs.append(output_item)
608 self.
logger.warning(f
'> Missing nodedef for node: {node.getNamePath()}')
612 json_node[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK] = outputs
614 return [procs, nodegraph_outputs, nodegraph_nodes]
618 @brief Convert a MaterialX document to glTF.
619 @param mtlx_doc: The MaterialX document to convert.
620 @return glTF JSON string and status message.
625 status =
'Invalid document to convert'
629 mx_materials = mtlx_doc.getMaterialNodes()
630 if len(mx_materials) == 0:
631 self.
logger.warning(
'> No materials found in document')
637 "generator":
"MaterialX 1.39 / glTF 2.0 Texture Procedural Converter"
642 input_maps[MTLX_GLTF_PBR_CATEGORY] = [
645 [
'base_color',
'baseColorTexture',
'pbrMetallicRoughness']
647 input_maps[MTLX_UNLIT_CATEGORY_STRING] = [[
'emission_color',
'baseColorTexture',
'pbrMetallicRoughness']]
650 fallback_texture_index = -1
651 fallback_image_data =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4z/AfAAQAAf/zKSWvAAAAAElFTkSuQmCC'
653 export_graph_names = []
655 extensions_used = [KHR_TEXTURE_PROCEDURALS, EXT_TEXTURE_PROCEDURALS_MX_1_39]
658 for mxMaterial
in mx_materials:
659 mtlx_shaders = mx.getShaderNodes(mxMaterial)
662 for shader_node
in mtlx_shaders:
663 category = shader_node.getCategory()
664 path = shader_node.getNamePath()
665 is_pbr = (category == MTLX_GLTF_PBR_CATEGORY)
666 is_unlit = (category == MTLX_UNLIT_CATEGORY_STRING)
668 if (is_pbr
or is_unlit)
and pbr_nodes.get(path)
is None:
670 if fallback_texture_index == -1:
673 self.
logger.info(f
'> Convert shader to glTF: {shader_node.getNamePath()}. Category: {category}')
674 pbr_nodes[path] = shader_node
678 material[KHR_TEXTURE_PROCEDURALS_NAME] = path
680 material[KHR_EXTENSIONS_BLOCK] = {}
681 material[KHR_EXTENSIONS_BLOCK][KHR_MATERIALX_UNLIT] = {}
683 if KHR_MATERIALX_UNLIT
not in extensions_used:
684 extensions_used.append(KHR_MATERIALX_UNLIT)
686 shader_node_input =
None
687 shader_node_output =
''
688 input_pairs = input_maps[category]
691 for input_pair
in input_pairs:
692 shader_node_input = shader_node.getInput(input_pair[0])
693 shader_node_output = input_pair[1]
695 if not shader_node_input:
699 nodegraph_name = shader_node_input.getNodeGraphString()
700 if len(nodegraph_name) == 0:
704 nodegraph_output = shader_node_input.getOutputString()
708 if len(input_pair[2]) > 0:
709 if input_pair[2]
not in material:
710 material[input_pair[2]] = {}
711 parent = material[input_pair[2]]
719 for i, proc
in enumerate(procs):
720 if proc[KHR_TEXTURE_PROCEDURALS_NAME] == nodegraph_name:
722 outputs_length = len(nodegraph_output)
723 if outputs_length > 0:
724 for j, output
in enumerate(proc[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK]):
725 if output[KHR_TEXTURE_PROCEDURALS_NAME] == nodegraph_output:
732 shader_input_texture = parent[shader_node_output] = {}
733 shader_input_texture[KHR_TEXTURE_PROCEDURALS_INDEX] = fallback_texture_index
734 ext = shader_input_texture[KHR_EXTENSIONS_BLOCK] = {}
738 lookup = ext[KHR_TEXTURE_PROCEDURALS] = {}
739 lookup[KHR_TEXTURE_PROCEDURALS_INDEX] = graph_index
740 if output_index >= 0
and outputs_length > 1:
741 lookup[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_index
745 graph = mtlx_doc.getNodeGraph(nodegraph_name)
746 export_graph_names.append(nodegraph_name)
750 output_nodes = gltf_info[1]
753 shader_input_texture = parent[shader_node_output] = {}
754 shader_input_texture[KHR_TEXTURE_PROCEDURALS_INDEX] = fallback_texture_index
755 ext = shader_input_texture[KHR_EXTENSIONS_BLOCK] = {}
756 lookup = ext[KHR_TEXTURE_PROCEDURALS] = {}
757 lookup[KHR_TEXTURE_PROCEDURALS_INDEX] = len(procs) - 1
761 if len(nodegraph_output) > 0:
762 nodegraph_outputPath = f
"{nodegraph_name}/{nodegraph_output}"
763 if nodegraph_outputPath
in output_nodes:
764 output_index = output_nodes[nodegraph_outputPath]
766 self.
logger.error(f
'> Failed to find output: {nodegraph_output} '
767 ' in: {output_nodes}')
768 lookup[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_index
770 lookup[KHR_TEXTURE_PROCEDURALS_OUTPUT] = 0
772 if KHR_TEXTURE_PROCEDURALS_NAME
in material:
773 materials.append(material)
776 unconnected_graphs = []
777 for ng
in mtlx_doc.getNodeGraphs():
778 ng_name = ng.getName()
779 if ng.getAttribute(MTLX_NODEDEF_NAME_ATTRIBUTE)
or ng.hasSourceUri():
781 if ng_name
not in export_graph_names:
782 unconnected_graphs.append(ng_name)
785 output_nodes = gltf_info[1]
787 if len(materials) > 0:
788 json_data[KHR_MATERIALS_BLOCK] = materials
789 if len(unconnected_graphs) > 0:
790 status =
'Exported unconnected graphs: ' +
', '.join(unconnected_graphs)
792 if len(unconnected_graphs) > 0:
793 status =
'Exported unconnected graphs: ' +
', '.join(unconnected_graphs)
795 status =
'No appropriate glTF shader graphs found'
798 images_block = json_data.get(KHR_IMAGES_BLOCK, [])
799 if len(images_block) == 0:
800 json_data.pop(KHR_IMAGES_BLOCK,
None)
802 textures_block = json_data.get(KHR_TEXTURES_BLOCK, [])
803 if len(textures_block) == 0:
804 json_data.pop(KHR_TEXTURES_BLOCK,
None)
807 if procs
and len(procs) > 0:
808 json_data[KHR_ASSET_BLOCK] = json_asset
809 json_data[KHR_EXTENTIONSUSED_BLOCK] = extensions_used
812 json_string = json.dumps(json_data, indent=2)
if json_data
else ''
813 if json_string ==
'{}':
817 status =
'No procedural graphs converted'
819 return json_string, status
915 Convert a glTF document to a MaterialX document.
916 @param gltFDoc: The glTF document to import.
917 @param stdlib: The MateriaLX standard library to use for the conversion.
918 @return The MaterialX document if successful, otherwise None.
921 self.
logger.error(
'> No glTF document specified')
925 if extension_check[0]
is None:
931 doc = mx.createDocument()
932 doc.setAttribute(
'colorspace',
'lin_rec709')
937 global_extensions = gltf_doc.get(
'extensions',
None)
939 if global_extensions
and KHR_TEXTURE_PROCEDURALS
in global_extensions:
940 procedurals = global_extensions[KHR_TEXTURE_PROCEDURALS].get(KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK,
None)
944 gltf_materials = gltf_doc.get(KHR_MATERIALS_BLOCK,
None)
948 input_maps[MTLX_GLTF_PBR_CATEGORY] = [
949 [
'base_color',
'baseColorTexture',
'pbrMetallicRoughness']
951 input_maps[MTLX_UNLIT_CATEGORY_STRING] = [[
'emission_color',
'baseColorTexture',
'pbrMetallicRoughness']]
953 for gltf_material
in gltf_materials:
955 mtlx_shader_name = gltf_material.get(KHR_TEXTURE_PROCEDURALS_NAME, MTLX_DEFAULT_SHADER_NAME)
956 mtlx_material_name =
''
957 if len(mtlx_shader_name) == 0:
958 mtlx_shader_name = MTLX_DEFAULT_SHADER_NAME
959 mtlx_material_name = MTLX_DEFAULT_MATERIAL_NAME
961 mtlx_material_name = MTLX_MATERIAL_PREFIX + mtlx_shader_name
963 mtlx_shader_name = doc.createValidChildName(mtlx_shader_name)
964 mtlx_material_name = doc.createValidChildName(mtlx_material_name)
967 extensions = gltf_material.get(
'extensions',
None)
968 if extensions
and KHR_MATERIALX_UNLIT
in extensions:
971 shader_category = MTLX_GLTF_PBR_CATEGORY
974 shader_category = MTLX_UNLIT_CATEGORY_STRING
975 nodedef_string =
'ND_surface_unlit'
977 shader_node = doc.addNode(shader_category, mtlx_shader_name, mx.SURFACE_SHADER_TYPE_STRING)
988 current_map = input_maps[shader_category]
989 for map_item
in current_map:
990 dest_input = map_item[0]
991 source_texture = map_item[1]
992 source_parent = map_item[2]
995 if source_parent ==
'pbrMetallicRoughness':
996 if 'pbrMetallicRoughness' in gltf_material:
997 source_texture = gltf_material[
'pbrMetallicRoughness'].get(source_texture,
None)
999 source_texture =
None
1001 source_texture = gltf_material.get(source_texture,
None)
1003 base_color_texture = source_texture
1005 if base_color_texture:
1006 extensions = base_color_texture.get(
'extensions',
None)
1007 if extensions
and KHR_TEXTURE_PROCEDURALS
in extensions:
1008 KHR_texture_procedurals = extensions[KHR_TEXTURE_PROCEDURALS]
1009 procedural_index = KHR_texture_procedurals.get(
'index',
None)
1010 output = KHR_texture_procedurals.get(
'output',
None)
1012 if procedural_index
is not None and procedural_index < len(procedurals):
1013 proc = procedurals[procedural_index]
1015 nodegraph_name = proc.get(
'name')
1016 graph_outputs = proc.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK,
None)
1017 output_count = len(graph_outputs)
1019 proc_output = graph_outputs[0]
1020 if output
is not None:
1021 proc_output = graph_outputs[output]
1024 input_node = shader_node.getInput(dest_input)
1026 input_node = shader_node.addInput(dest_input, proc_output[KHR_TEXTURE_PROCEDURALS_TYPE])
1029 input_node.removeAttribute(
'value')
1030 input_node.setNodeGraphString(nodegraph_name)
1031 if output_count > 1:
1032 input_node.setAttribute(
'output', proc_output[KHR_TEXTURE_PROCEDURALS_NAME])
1035 material_node = doc.addNode(mx.SURFACE_MATERIAL_NODE_STRING, mtlx_material_name, mx.MATERIAL_TYPE_STRING)
1036 shader_input = material_node.addInput(mx.SURFACE_SHADER_TYPE_STRING, mx.SURFACE_SHADER_TYPE_STRING)
1037 shader_input.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, mtlx_shader_name)
1039 self.
logger.info(f
'> Import material: {material_node.getName()}. Shader: {shader_node.getName()}')
1043 asset = gltf_doc.get(
'asset',
None)
1044 mtlx_doc_string =
''
1046 version = asset.get(
'version',
None)
1048 mtlx_doc_string += f
'glTF version: {version}. '
1050 generator = asset.get(
'generator',
None)
1052 mtlx_doc_string += f
'glTF generator: {generator}. '
1054 copyRight = asset.get(
'copyright',
None)
1056 mtlx_doc_string += f
'Copyright: {copyRight}. '
1059 doc.setDocString(mtlx_doc_string)
1096 Create names for all procedural graphs and materials if they don't already exist.
1097 This method should always be run before converting a glTF document to MaterialX to
1098 ensure that all connection references to elements are also handled.
1099 @param gltf_doc: The glTF document to clear the names in.
1102 dummy_doc = mx.createDocument()
1104 extensions = gltf_doc.get(
'extensions',
None)
1107 procedurals = extensions.get(KHR_TEXTURE_PROCEDURALS, {}).get(KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK,
None)
1111 for proc
in procedurals:
1113 proc_name = proc.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1114 if len(proc_name) == 0:
1115 proc_name = MTLX_DEFAULT_GRAPH_NAME
1116 proc[
'name'] = dummy_doc.createValidChildName(proc_name)
1118 dummy_graph = dummy_doc.addNodeGraph(proc[
'name'])
1120 inputs = proc.get(KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, [])
1121 outputs = proc.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, [])
1122 nodes = proc.get(KHR_TEXTURE_PROCEDURALS_NODES_BLOCK, [])
1125 for input_item
in inputs:
1126 input_name = input_item.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1127 if len(input_name) == 0:
1128 input_name = MTLX_DEFAULT_INPUT_NAME
1129 input_item[
'name'] = dummy_graph.createValidChildName(input_name)
1130 self.
logger.debug(
'Add input:' + input_item[
'name'])
1131 dummy_graph.addInput(input_item[
'name'])
1134 for output_item
in outputs:
1135 output_name = output_item.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1136 if len(output_name) == 0:
1137 output_name = MTLX_DEFAULT_OUTPUT_NAME
1138 output_item[
'name'] = dummy_graph.createValidChildName(output_name)
1139 self.
logger.debug(
'Add output:' + output_item[
'name'])
1140 dummy_graph.addOutput(output_item[
'name'])
1144 node_name = node.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1145 if len(node_name) == 0:
1146 node_name = MTLX_DEFAULT_NODE_NAME
1147 node[
'name'] = dummy_graph.createValidChildName(node_name)
1149 node_type = node.get(
'nodetype',
None)
1150 dummy_graph.addChildOfCategory(node_type, node[
'name'])
1153 materials = gltf_doc.get(KHR_MATERIALS_BLOCK,
None)
1155 for material
in materials:
1156 material_name = material.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1157 if len(material_name) == 0:
1158 material_name = MTLX_DEFAULT_SHADER_NAME
1159 material[
'name'] = dummy_doc.createValidChildName(material_name)
1160 dummy_doc.addNode(MTLX_GLTF_PBR_CATEGORY, material[
'name'], mx.SURFACE_SHADER_TYPE_STRING)
1164 Import the procedural graphs from a glTF document into a MaterialX document.
1165 @param doc: The MaterialX document to import the graphs into.
1166 @param gltf_doc: The glTF document to import the graphs from.
1167 @return The root MaterialX nodegraph if successful, otherwise None.
1172 extensions = gltf_doc.get(
'extensions',
None)
1175 procedurals = extensions.get(KHR_TEXTURE_PROCEDURALS, {}).get(KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK,
None)
1177 if procedurals
is None:
1178 self.
logger.error(
'> No procedurals array found')
1186 self.
logger.info(f
'> Importing {len(procedurals)} procedural graphs')
1187 for proc
in procedurals:
1188 self.
logger.debug(f
'> Scan procedural {graph_index} of {len(procedurals)} :')
1189 if not proc.get(
'nodetype'):
1190 self.
logger.warning(
'> No nodetype found in procedural. Skipping node')
1193 if proc[KHR_TEXTURE_PROCEDURALS_NODETYPE] !=
'nodegraph':
1194 self.
logger.warning(f
'> Unsupported procedural nodetype found: {proc["nodetype"]}')
1198 graph_name = proc.get(
'name',
'GRAPH_' + str(graph_index))
1199 if len(graph_name) == 0:
1200 graph_name =
'GRAPH_' + str(graph_index)
1201 graph_name = doc.createValidChildName(graph_name)
1202 proc[
'name'] = graph_name
1205 self.
logger.info(f
'> Create new nodegraph: {graph_name}')
1206 mtlx_graph = doc.addNodeGraph(graph_name)
1208 for meta
in graph_metadata:
1210 proc_meta_data = proc[meta]
1211 self.
logger.debug(f
'> Add extra graph attribute: {meta}, {proc_meta_data}')
1212 mtlx_graph.setAttribute(meta, proc_meta_data)
1214 root_mtlx = mtlx_graph
1216 inputs = proc.get(KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, [])
1217 outputs = proc.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, [])
1218 nodes = proc.get(KHR_TEXTURE_PROCEDURALS_NODES_BLOCK, [])
1222 for input_item
in inputs:
1223 input_name = input_item.get(
'name', MTLX_DEFAULT_INPUT_NAME)
1224 if len(input_name) == 0:
1225 input_name = MTLX_DEFAULT_INPUT_NAME
1226 input_item[
'name'] = mtlx_graph.createValidChildName(input_name)
1227 for output_item
in outputs:
1228 output_name = output_item.get(
'name', MTLX_DEFAULT_INPUT_NAME)
1229 if len(output_name) == 0:
1230 output_name = MTLX_DEFAULT_OUTPUT_NAME
1231 output_item[
'name'] = mtlx_graph.createValidChildName(output_name)
1233 node_name = node.get(KHR_TEXTURE_PROCEDURALS_NAME, MTLX_DEFAULT_NODE_NAME)
1234 if len(node_name) == 0:
1235 node_name = MTLX_DEFAULT_NODE_NAME
1236 node[
'name'] = mtlx_graph.createValidChildName(node_name)
1239 self.
logger.debug(f
'> Scan {len(inputs)} inputs')
1240 for input_item
in inputs:
1241 inputname = input_item.get(
'name',
None)
1244 input_type = input_item.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1246 self.
logger.error(f
'> Input type not found for graph input: {inputname}')
1250 mtlx_input = mtlx_graph.addInput(inputname, input_type)
1252 for meta
in metadata:
1253 if meta
in input_item:
1254 self.
logger.debug(f
'> Add extra interface attribute: {meta}, {input_item[meta]}')
1255 mtlx_input.setAttribute(meta, input_item[meta])
1258 if input_type ==
'filename':
1259 texture_index = input_item.get(
'texture',
None)
1260 if texture_index
is not None:
1261 gltf_textures = gltf_doc.get(
'textures',
None)
1262 gltf_images = gltf_doc.get(
'images',
None)
1263 if gltf_textures
and gltf_images:
1264 gltf_texture = gltf_textures[texture_index]
if texture_index < len(gltf_textures)
else None
1267 mtlx_input.setValueString(uri, input_type)
1270 input_value = input_item.get(
'value',
None)
1271 if input_value
is not None:
1273 if mtlx_value
is not None:
1274 mtlx_input.setValueString(mtlx_value)
1275 mtlx_input.setType(input_type)
1277 mtlx_input.setValueString(str(input_value))
1279 self.
logger.error(f
'> Interface input has no value specified: {inputname}')
1282 self.
logger.debug(f
'> Scan {len(nodes)} nodes')
1284 node_name = node.get(KHR_TEXTURE_PROCEDURALS_NAME,
None)
1285 node_type = node.get(
'nodetype',
None)
1286 output_type = node.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1287 node_outputs = node.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, [])
1290 mtlx_node = mtlx_graph.addChildOfCategory(node_type, node_name)
1293 if len(node_outputs) > 1:
1294 output_type = MULTI_OUTPUT_TYPE_STRING
1300 mtlx_node.setType(output_type)
1302 self.
logger.error(f
'> No output type specified for node: {node_name}')
1306 for key, value
in node.items():
1307 if key
not in [KHR_TEXTURE_PROCEDURALS_NAME,
'nodetype', KHR_TEXTURE_PROCEDURALS_TYPE, KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK]:
1308 self.
logger.debug(f
'> Add extra node attribute: {key}, {value}')
1309 mtlx_node.setAttribute(key, value)
1312 node_inputs = node.get(KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, [])
1313 for input_item
in node_inputs:
1314 input_name = input_item.get(
'name', MTLX_DEFAULT_INPUT_NAME)
1315 input_type = input_item.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1318 mtlx_input = mtlx_node.addInput(input_name, input_type)
1321 if input_type ==
'filename':
1322 texture_index = input_item.get(
'texture',
None)
1323 if texture_index
is not None:
1324 gltf_textures = gltf_doc.get(
'textures',
None)
1325 gltf_images = gltf_doc.get(
'images',
None)
1326 if gltf_textures
and gltf_images:
1327 gltftexture = gltf_textures[texture_index]
if texture_index < len(gltf_textures)
else None
1330 mtlx_input.setValueString(uri)
1333 input_value = input_item.get(
'value',
None)
1334 if input_value
is not None:
1336 if mtlx_value
is not None:
1337 mtlx_input.setValueString(mtlx_value)
1338 mtlx_input.setType(input_type)
1340 self.
logger.error(f
'> Unsupported input type: {input_type}. Performing straight assignment.')
1341 mtlx_input.setValueString(str(input_value))
1348 if 'input' in input_item:
1349 connectable = inputs[input_item[KHR_TEXTURE_PROCEDURALS_INPUT]]
if input_item[KHR_TEXTURE_PROCEDURALS_INPUT] < len(inputs)
else None
1350 mtlx_input.setAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1353 elif 'output' in input_item:
1354 if 'node' not in input_item:
1355 connectable = outputs[input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT]]
if input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT] < len(outputs)
else None
1356 mtlx_input.setAttribute(
'output', connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1359 if 'node' in input_item:
1360 connectable = nodes[input_item[KHR_TEXTURE_PROCEDURALS_NODE]]
if input_item[KHR_TEXTURE_PROCEDURALS_NODE] < len(nodes)
else None
1361 mtlx_input.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1363 if 'output' in input_item:
1365 connectable = nodes[input_item[KHR_TEXTURE_PROCEDURALS_NODE]]
if input_item[KHR_TEXTURE_PROCEDURALS_NODE] < len(nodes)
else None
1368 connected_outputs = connectable.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, [])
1369 if connected_outputs:
1370 output_index = input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT]
1371 output_string = connected_outputs[output_index][KHR_TEXTURE_PROCEDURALS_NAME]
1372 mtlx_input.setAttribute(
'output', output_string)
1373 self.
logger.debug(f
'Set output specifier on input: {mtlx_input.getNamePath()}. Value: {input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT]}')
1376 for meta
in metadata:
1377 if meta
in input_item:
1378 self.
logger.debug(f
'> Add extra input attribute: {meta}, {input_item[meta]}')
1379 mtlx_input.setAttribute(meta, input_item[meta])
1382 if len(node_outputs) > 1:
1383 for output
in node_outputs:
1384 output_name = output.get(
'name')
1385 output_type = output.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1386 mtlxoutput = mtlx_node.addOutput(output_name, output_type)
1387 self.
logger.debug(f
'Add multioutput output {mtlxoutput.getNamePath()} of type {output_type} to node {node_name}')
1392 self.
logger.info(f
'> Scan {len(outputs)} procedural outputs')
1393 for output
in outputs:
1394 output_name = output.get(
'name',
None)
1395 output_type = output.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1396 mtlx_graph_output = mtlx_graph.addOutput(output_name, output_type)
1401 if 'input' in output:
1402 connectable = inputs[output[KHR_TEXTURE_PROCEDURALS_INPUT]]
if output[KHR_TEXTURE_PROCEDURALS_INPUT] < len(inputs)
else None
1404 mtlx_graph_output.setAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1406 self.
logger.error(f
'Input not found: {output["input"]}, {inputs}')
1409 elif 'node' in output:
1410 connectable = nodes[output[KHR_TEXTURE_PROCEDURALS_NODE]]
if output[KHR_TEXTURE_PROCEDURALS_NODE] < len(nodes)
else None
1412 mtlx_graph_output.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1413 if 'output' in output:
1414 mtlx_graph_output.setAttribute(
'output', output[KHR_TEXTURE_PROCEDURALS_OUTPUT])
1416 self.
logger.error(f
'> Output node not found: {output["node"]}, {nodes}')
1419 for key, value
in output.items():
1420 if key
not in [KHR_TEXTURE_PROCEDURALS_NAME, KHR_TEXTURE_PROCEDURALS_TYPE,
'nodetype',
'node',
'output']:
1421 self.
logger.debug(f
'> Add extra graph output attribute: {key}. Value: {value}')
1422 mtlx_graph_output.setAttribute(key, value)