318 Add a fallback texture to the glTF JSON object.
319 @param json: The JSON object to add the fallback texture to.
320 @param fallback: The fallback texture URI.
321 @return The index of the fallback texture if successful, otherwise -1.
323 fallback_texture_index = -1
324 fallback_image_index = -1
326 images_block = json.get(KHR_IMAGES_BLOCK, [])
327 if KHR_IMAGES_BLOCK
not in json:
328 json[KHR_IMAGES_BLOCK] = images_block
330 for i, image
in enumerate(images_block):
331 if image[KHR_IMAGE_URI] == fallback:
332 fallback_image_index = i
335 if fallback_image_index == -1:
337 KHR_IMAGE_URI: fallback,
338 KHR_TEXTURE_PROCEDURALS_NAME:
'KHR_texture_procedural_fallback'
340 images_block.append(image)
341 fallback_image_index = len(images_block) - 1
343 texture_array = json.get(KHR_TEXTURES_BLOCK, [])
344 if KHR_TEXTURES_BLOCK
not in json:
345 json[KHR_TEXTURES_BLOCK] = texture_array
347 for i, texture
in enumerate(texture_array):
348 if texture[KHR_IMAGE_SOURCE] == fallback_image_index:
349 fallback_texture_index = i
352 if fallback_texture_index == -1:
353 texture_array.append({KHR_IMAGE_SOURCE: fallback_image_index})
354 fallback_texture_index = len(texture_array) - 1
356 return fallback_texture_index
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():
432 input_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][input_name] = json_node
459 nodegraph_inputs[input.getNamePath()] = input_name
461 self.
logger.error(f
'> No value or invalid connection specified for input. Input skipped: {input.getNamePath()}')
465 for output
in graph_outputs:
466 for output
in graph_outputs:
467 output_name = output.getNamePath()
if use_paths
else output.getName()
469 json_node[KHR_TEXTURE_PROCEDURALS_NODETYPE] = output.getCategory()
470 json_node[KHR_TEXTURE_PROCEDURALS_TYPE] = output.getType()
473 for meta
in metadata:
474 if output.getAttribute(meta):
475 json_node[meta] = output.getAttribute(meta)
479 connection = output.getAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE)
480 if len(connection) == 0:
481 connection = output.getAttribute(MTLX_NODE_NAME_ATTRIBUTE)
483 connection_node = graph.getChild(connection)
485 connection_path = connection_node.getNamePath()
487 json_node[
'debug_connection_path'] = connection_path
490 if nodegraph_inputs.get(connection_path)
is not None:
491 json_node[KHR_TEXTURE_PROCEDURALS_INPUT] = nodegraph_inputs[connection_path]
492 elif nodegraph_nodes.get(connection_path)
is not None:
493 json_node[KHR_TEXTURE_PROCEDURALS_NODE] = nodegraph_nodes[connection_path]
495 self.
logger.error(f
'> Invalid output connection to: {connection_path}')
498 output_string = output.getAttribute(MTLX_OUTPUT_ATTRIBUTE)
499 if len(output_string) > 0:
500 json_node[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_string
502 nodegraph[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK][output_name] = json_node
505 nodegraph_outputs[output.getNamePath()] = output_name
508 for node
in graph.getNodes():
509 json_node = nodegraph[KHR_TEXTURE_PROCEDURALS_NODES_BLOCK][nodegraph_nodes[node.getNamePath()]]
510 json_node[KHR_TEXTURE_PROCEDURALS_NODETYPE] = node.getCategory()
511 nodedef = node.getNodeDef()
515 self.
logger.error(f
'> Missing nodedef for node: {node.getNamePath()}')
518 if debug
and nodedef
and nodedef.getNodeGroup():
519 json_node[KHR_TEXTURE_PROCEDURALS_NODEGROUP] = nodedef.getNodeGroup()
521 for attr_name
in node.getAttributeNames():
522 json_node[attr_name] = node.getAttribute(attr_name)
527 for input
in node.getInputs():
528 input_name = input.getNamePath()
if use_paths
else input.getName()
533 for meta
in metadata:
534 if input.getAttribute(meta):
535 input_item[meta] = input.getAttribute(meta)
537 input_type = input.getAttribute(mx.TypedElement.TYPE_ATTRIBUTE)
538 input_item[KHR_TEXTURE_PROCEDURALS_TYPE] = input_type
543 connection = input.getAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE)
546 connection = input.getAttribute(MTLX_NODE_NAME_ATTRIBUTE)
549 connection_node = graph.getChild(connection)
551 connection_path = connection_node.getNamePath()
553 input_item[
'debug_connection_path'] = connection_path
555 if is_interface
and nodegraph_inputs.get(connection_path)
is not None:
556 input_item[KHR_TEXTURE_PROCEDURALS_INPUT] = nodegraph_inputs[connection_path]
557 elif nodegraph_nodes.get(connection_path)
is not None:
558 input_item[KHR_TEXTURE_PROCEDURALS_NODE] = nodegraph_nodes[connection_path]
560 output_string = input.getAttribute(MTLX_OUTPUT_ATTRIBUTE)
562 input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_string
569 self.
logger.error(f
'> Invalid input connection to: '
570 '{connection} from input: {input.getNamePath()} '
571 'node: {node.getNamePath()}')
574 elif input.getValue()
is not None:
575 if input_type == mx.FILENAME_TYPE_STRING:
577 filename = input.getResolvedValueString()
579 texture_array.append(texture)
580 input_item[KHR_TEXTURE_PROCEDURALS_TEXTURE] = len(texture_array) - 1
582 value = input.getValueString()
584 input_item[KHR_TEXTURE_PROCEDURALS_VALUE] = value
586 inputs[input_name] = input_item
590 json_node[KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK] = inputs
594 for output
in node.getOutputs():
595 output_name = output.getName()
597 'nodetype': KHR_TEXTURE_PROCEDURALS_OUTPUT,
598 KHR_TEXTURE_PROCEDURALS_TYPE: output.getType()
600 outputs[output_name] = output_item
604 for output
in nodedef.getOutputs():
605 if not any(output_item != output.getName()
for output_item
in outputs):
607 'nodetype': KHR_TEXTURE_PROCEDURALS_OUTPUT,
608 KHR_TEXTURE_PROCEDURALS_TYPE: output.getType()
610 outputs[output.getName()] = output_item
612 self.
logger.warning(f
'> Missing nodedef for node: {node.getNamePath()}')
616 json_node[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK] = outputs
618 return [procs, nodegraph_outputs, nodegraph_nodes]
622 @brief Convert a MaterialX document to glTF.
623 @param mtlx_doc: The MaterialX document to convert.
624 @return glTF JSON string and status message.
629 status =
'Invalid document to convert'
633 mx_materials = mtlx_doc.getMaterialNodes()
634 if len(mx_materials) == 0:
635 self.
logger.warning(
'> No materials found in document')
641 "generator":
"MaterialX 1.39 / glTF 2.0 Texture Procedural Converter"
646 input_maps[MTLX_GLTF_PBR_CATEGORY] = [
651 [
'base_color',
'baseColorTexture',
'pbrMetallicRoughness'],
652 [
'metallic',
'metallicRoughnessTexture',
'pbrMetallicRoughness'],
653 [
'roughness',
'metallicRoughnessTexture',
'pbrMetallicRoughness'],
654 [
'normal',
'normalTexture',
''],
655 [
'occlusion',
'occlusionTexture',
''],
656 [
'emissive',
'emissiveTexture',
'']
658 input_maps[MTLX_UNLIT_CATEGORY_STRING] = [[
'emission_color',
'baseColorTexture',
'pbrMetallicRoughness']]
661 fallback_texture_index = -1
662 fallback_image_data =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4z/AfAAQAAf/zKSWvAAAAAElFTkSuQmCC'
664 export_graph_names = []
666 extensions_used = [KHR_TEXTURE_PROCEDURALS, EXT_TEXTURE_PROCEDURALS_MX_1_39]
669 for mxMaterial
in mx_materials:
670 mtlx_shaders = mx.getShaderNodes(mxMaterial)
673 for shader_node
in mtlx_shaders:
674 category = shader_node.getCategory()
675 path = shader_node.getNamePath()
676 is_pbr = (category == MTLX_GLTF_PBR_CATEGORY)
677 is_unlit = (category == MTLX_UNLIT_CATEGORY_STRING)
679 if (is_pbr
or is_unlit)
and pbr_nodes.get(path)
is None:
681 if fallback_texture_index == -1:
684 self.
logger.info(f
'> Convert shader to glTF: {shader_node.getNamePath()}. Category: {category}')
685 pbr_nodes[path] = shader_node
689 material[KHR_TEXTURE_PROCEDURALS_NAME] = path
691 material[KHR_EXTENSIONS_BLOCK] = {}
692 material[KHR_EXTENSIONS_BLOCK][KHR_MATERIALX_UNLIT] = {}
694 if KHR_MATERIALX_UNLIT
not in extensions_used:
695 extensions_used.append(KHR_MATERIALX_UNLIT)
697 shader_node_input =
None
698 shader_node_output =
''
699 input_pairs = input_maps[category]
702 for input_pair
in input_pairs:
703 shader_node_input = shader_node.getInput(input_pair[0])
704 shader_node_output = input_pair[1]
706 if not shader_node_input:
710 nodegraph_name = shader_node_input.getNodeGraphString()
711 if len(nodegraph_name) == 0:
715 nodegraph_output = shader_node_input.getOutputString()
719 if len(input_pair[2]) > 0:
720 if input_pair[2]
not in material:
721 material[input_pair[2]] = {}
722 parent = material[input_pair[2]]
730 for i, proc
in enumerate(procs):
731 if proc[KHR_TEXTURE_PROCEDURALS_NAME] == nodegraph_name:
733 outputs_length = len(nodegraph_output)
734 if outputs_length > 0:
735 outputs_list = proc[KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK]
736 for test_name, item
in outputs_list.items():
737 if test_name == nodegraph_output:
738 output_name = test_name
744 shader_input_texture = parent[shader_node_output] = {}
745 shader_input_texture[KHR_TEXTURE_PROCEDURALS_INDEX] = fallback_texture_index
746 ext = shader_input_texture[KHR_EXTENSIONS_BLOCK] = {}
750 lookup = ext[KHR_TEXTURE_PROCEDURALS] = {}
751 lookup[KHR_TEXTURE_PROCEDURALS_INDEX] = graph_index
753 lookup[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_name
757 graph = mtlx_doc.getNodeGraph(nodegraph_name)
758 export_graph_names.append(nodegraph_name)
762 output_nodes = gltf_info[1]
765 shader_input_texture = parent[shader_node_output] = {}
766 shader_input_texture[KHR_TEXTURE_PROCEDURALS_INDEX] = fallback_texture_index
767 ext = shader_input_texture[KHR_EXTENSIONS_BLOCK] = {}
768 lookup = ext[KHR_TEXTURE_PROCEDURALS] = {}
769 lookup[KHR_TEXTURE_PROCEDURALS_INDEX] = len(procs) - 1
773 if len(nodegraph_output) > 0:
774 nodegraph_outputPath = f
"{nodegraph_name}/{nodegraph_output}"
775 if nodegraph_outputPath
in output_nodes:
776 output_name = output_nodes[nodegraph_outputPath]
777 lookup[KHR_TEXTURE_PROCEDURALS_OUTPUT] = output_name
779 self.
logger.error(f
'> Failed to find output: {nodegraph_output} '
780 ' in: {output_nodes}')
783 lookup[KHR_TEXTURE_PROCEDURALS_OUTPUT] = next(iter(output_nodes.values()))
785 if KHR_TEXTURE_PROCEDURALS_NAME
in material:
786 materials.append(material)
789 unconnected_graphs = []
790 for ng
in mtlx_doc.getNodeGraphs():
791 ng_name = ng.getName()
792 if ng.getAttribute(MTLX_NODEDEF_NAME_ATTRIBUTE)
or ng.hasSourceUri():
794 if ng_name
not in export_graph_names:
795 unconnected_graphs.append(ng_name)
798 output_nodes = gltf_info[1]
800 if len(materials) > 0:
801 json_data[KHR_MATERIALS_BLOCK] = materials
802 if len(unconnected_graphs) > 0:
803 status =
'Exported unconnected graphs: ' +
', '.join(unconnected_graphs)
805 if len(unconnected_graphs) > 0:
806 status =
'Exported unconnected graphs: ' +
', '.join(unconnected_graphs)
808 status =
'No appropriate glTF shader graphs found'
811 images_block = json_data.get(KHR_IMAGES_BLOCK, [])
812 if len(images_block) == 0:
813 json_data.pop(KHR_IMAGES_BLOCK,
None)
815 textures_block = json_data.get(KHR_TEXTURES_BLOCK, [])
816 if len(textures_block) == 0:
817 json_data.pop(KHR_TEXTURES_BLOCK,
None)
820 if procs
and len(procs) > 0:
821 json_data[KHR_ASSET_BLOCK] = json_asset
822 json_data[KHR_EXTENTIONSUSED_BLOCK] = extensions_used
825 json_string = json.dumps(json_data, indent=2)
if json_data
else ''
826 if json_string ==
'{}':
830 status =
'No procedural graphs converted'
832 return json_string, status
931 Convert a glTF document to a MaterialX document.
932 @param gltFDoc: The glTF document to import.
933 @param stdlib: The MateriaLX standard library to use for the conversion.
934 @return The MaterialX document if successful, otherwise None.
937 self.
logger.error(
'> No glTF document specified')
941 if extension_check[0]
is None:
947 doc = mx.createDocument()
948 doc.setAttribute(
'colorspace',
'lin_rec709')
953 global_extensions = gltf_doc.get(
'extensions',
None)
955 if global_extensions
and KHR_TEXTURE_PROCEDURALS
in global_extensions:
956 procedurals = global_extensions[KHR_TEXTURE_PROCEDURALS].get(KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK,
None)
960 gltf_materials = gltf_doc.get(KHR_MATERIALS_BLOCK,
None)
964 input_maps[MTLX_GLTF_PBR_CATEGORY] = [
965 [
'base_color',
'baseColorTexture',
'pbrMetallicRoughness']
967 input_maps[MTLX_UNLIT_CATEGORY_STRING] = [[
'emission_color',
'baseColorTexture',
'pbrMetallicRoughness']]
969 for gltf_material
in gltf_materials:
971 mtlx_shader_name = gltf_material.get(KHR_TEXTURE_PROCEDURALS_NAME, MTLX_DEFAULT_SHADER_NAME)
972 mtlx_material_name =
''
973 if len(mtlx_shader_name) == 0:
974 mtlx_shader_name = MTLX_DEFAULT_SHADER_NAME
975 mtlx_material_name = MTLX_DEFAULT_MATERIAL_NAME
977 mtlx_material_name = MTLX_MATERIAL_PREFIX + mtlx_shader_name
979 mtlx_shader_name = doc.createValidChildName(mtlx_shader_name)
980 mtlx_material_name = doc.createValidChildName(mtlx_material_name)
983 extensions = gltf_material.get(
'extensions',
None)
984 if extensions
and KHR_MATERIALX_UNLIT
in extensions:
987 shader_category = MTLX_GLTF_PBR_CATEGORY
990 shader_category = MTLX_UNLIT_CATEGORY_STRING
991 nodedef_string =
'ND_surface_unlit'
993 shader_node = doc.addNode(shader_category, mtlx_shader_name, mx.SURFACE_SHADER_TYPE_STRING)
1004 current_map = input_maps[shader_category]
1005 for map_item
in current_map:
1006 dest_input = map_item[0]
1007 source_texture = map_item[1]
1008 source_parent = map_item[2]
1011 if source_parent ==
'pbrMetallicRoughness':
1012 if 'pbrMetallicRoughness' in gltf_material:
1013 source_texture = gltf_material[
'pbrMetallicRoughness'].get(source_texture,
None)
1015 source_texture =
None
1017 source_texture = gltf_material.get(source_texture,
None)
1019 base_color_texture = source_texture
1021 if base_color_texture:
1022 extensions = base_color_texture.get(
'extensions',
None)
1023 if extensions
and KHR_TEXTURE_PROCEDURALS
in extensions:
1024 KHR_texture_procedurals = extensions[KHR_TEXTURE_PROCEDURALS]
1025 procedural_index = KHR_texture_procedurals.get(
'index',
None)
1026 output = KHR_texture_procedurals.get(
'output',
None)
1028 if procedural_index
is not None and procedural_index < len(procedurals):
1029 proc = procedurals[procedural_index]
1031 nodegraph_name = proc.get(
'name')
1032 graph_outputs = proc.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK,
None)
1033 output_count = len(graph_outputs)
1035 if output
is not None:
1037 for key, value
in graph_outputs.items():
1043 input_node = shader_node.getInput(dest_input)
1045 input_node = shader_node.addInput(dest_input, proc_output[KHR_TEXTURE_PROCEDURALS_TYPE])
1048 input_node.removeAttribute(
'value')
1049 input_node.setNodeGraphString(nodegraph_name)
1050 if output_count > 1:
1051 input_node.setAttribute(
'output', proc_output[KHR_TEXTURE_PROCEDURALS_NAME])
1054 material_node = doc.addNode(mx.SURFACE_MATERIAL_NODE_STRING, mtlx_material_name, mx.MATERIAL_TYPE_STRING)
1055 shader_input = material_node.addInput(mx.SURFACE_SHADER_TYPE_STRING, mx.SURFACE_SHADER_TYPE_STRING)
1056 shader_input.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, mtlx_shader_name)
1058 self.
logger.info(f
'> Import material: {material_node.getName()}. Shader: {shader_node.getName()}')
1062 asset = gltf_doc.get(
'asset',
None)
1063 mtlx_doc_string =
''
1065 version = asset.get(
'version',
None)
1067 mtlx_doc_string += f
'glTF version: {version}. '
1069 generator = asset.get(
'generator',
None)
1071 mtlx_doc_string += f
'glTF generator: {generator}. '
1073 copyRight = asset.get(
'copyright',
None)
1075 mtlx_doc_string += f
'Copyright: {copyRight}. '
1078 doc.setDocString(mtlx_doc_string)
1116 Create names for all procedural graphs and materials if they don't already exist.
1117 This method should always be run before converting a glTF document to MaterialX to
1118 ensure that all connection references to elements are also handled.
1119 @param gltf_doc: The glTF document to clear the names in.
1122 dummy_doc = mx.createDocument()
1124 extensions = gltf_doc.get(
'extensions',
None)
1127 procedurals = extensions.get(KHR_TEXTURE_PROCEDURALS, {}).get(KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK,
None)
1131 for proc
in procedurals:
1133 proc_name = proc.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1134 if len(proc_name) == 0:
1135 proc_name = MTLX_DEFAULT_GRAPH_NAME
1136 proc[
'name'] = dummy_doc.createValidChildName(proc_name)
1138 dummy_graph = dummy_doc.addNodeGraph(proc[
'name'])
1140 inputs = proc.get(KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, {})
1141 outputs = proc.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, {})
1142 nodes = proc.get(KHR_TEXTURE_PROCEDURALS_NODES_BLOCK, [])
1146 for input_item
in inputs:
1147 input_name = input_item.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1148 if len(input_name) == 0:
1149 input_name = MTLX_DEFAULT_INPUT_NAME
1150 input_item[
'name'] = dummy_graph.createValidChildName(input_name)
1151 self.
logger.debug(
'Add input:' + input_item[
'name'])
1152 dummy_graph.addInput(input_item[
'name'])
1155 for output_item
in outputs:
1156 output_name = output_item.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1157 if len(output_name) == 0:
1158 output_name = MTLX_DEFAULT_OUTPUT_NAME
1159 output_item[
'name'] = dummy_graph.createValidChildName(output_name)
1160 self.
logger.debug(
'Add output:' + output_item[
'name'])
1161 dummy_graph.addOutput(output_item[
'name'])
1165 node_name = node.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1166 if len(node_name) == 0:
1167 node_name = MTLX_DEFAULT_NODE_NAME
1168 node[
'name'] = dummy_graph.createValidChildName(node_name)
1170 node_type = node.get(
'nodetype',
None)
1171 dummy_graph.addChildOfCategory(node_type, node[
'name'])
1174 materials = gltf_doc.get(KHR_MATERIALS_BLOCK,
None)
1176 for material
in materials:
1177 material_name = material.get(KHR_TEXTURE_PROCEDURALS_NAME,
'')
1178 if len(material_name) == 0:
1179 material_name = MTLX_DEFAULT_SHADER_NAME
1180 material[
'name'] = dummy_doc.createValidChildName(material_name)
1181 dummy_doc.addNode(MTLX_GLTF_PBR_CATEGORY, material[
'name'], mx.SURFACE_SHADER_TYPE_STRING)
1185 Import the procedural graphs from a glTF document into a MaterialX document.
1186 @param doc: The MaterialX document to import the graphs into.
1187 @param gltf_doc: The glTF document to import the graphs from.
1188 @return The root MaterialX nodegraph if successful, otherwise None.
1193 extensions = gltf_doc.get(
'extensions',
None)
1196 procedurals = extensions.get(KHR_TEXTURE_PROCEDURALS, {}).get(KHR_TEXTURE_PROCEDURALS_PROCEDURALS_BLOCK,
None)
1198 if procedurals
is None:
1199 self.
logger.error(
'> No procedurals array found')
1207 self.
logger.info(f
'> Importing {len(procedurals)} procedural graphs')
1208 for proc
in procedurals:
1209 self.
logger.debug(f
'> Scan procedural {graph_index} of {len(procedurals)} :')
1210 if not proc.get(
'nodetype'):
1211 self.
logger.warning(
'> No nodetype found in procedural. Skipping node')
1214 if proc[KHR_TEXTURE_PROCEDURALS_NODETYPE] !=
'nodegraph':
1215 self.
logger.warning(f
'> Unsupported procedural nodetype found: {proc["nodetype"]}')
1219 graph_name = proc.get(
'name',
'GRAPH_' + str(graph_index))
1220 if len(graph_name) == 0:
1221 graph_name =
'GRAPH_' + str(graph_index)
1222 graph_name = doc.createValidChildName(graph_name)
1223 proc[
'name'] = graph_name
1226 self.
logger.info(f
'> Create new nodegraph: {graph_name}')
1227 mtlx_graph = doc.addNodeGraph(graph_name)
1229 for meta
in graph_metadata:
1231 proc_meta_data = proc[meta]
1232 self.
logger.debug(f
'> Add extra graph attribute: {meta}, {proc_meta_data}')
1233 mtlx_graph.setAttribute(meta, proc_meta_data)
1235 root_mtlx = mtlx_graph
1237 inputs = proc.get(KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, {})
1238 outputs = proc.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, {})
1239 nodes = proc.get(KHR_TEXTURE_PROCEDURALS_NODES_BLOCK, [])
1243 for input_name, input_item
in inputs.items():
1244 if len(input_name) == 0:
1245 input_name = MTLX_DEFAULT_INPUT_NAME
1246 input_item[
'name'] = mtlx_graph.createValidChildName(input_name)
1247 if len(input_name) == 0:
1248 input_name = MTLX_DEFAULT_INPUT_NAME
1249 input_item[
'name'] = mtlx_graph.createValidChildName(input_name)
1250 for output_name, output_item
in outputs.items():
1251 if len(output_name) == 0:
1252 output_name = MTLX_DEFAULT_OUTPUT_NAME
1253 output_item[
'name'] = mtlx_graph.createValidChildName(output_name)
1255 node_name = node.get(KHR_TEXTURE_PROCEDURALS_NAME, MTLX_DEFAULT_NODE_NAME)
1256 if len(node_name) == 0:
1257 node_name = MTLX_DEFAULT_NODE_NAME
1258 node[
'name'] = mtlx_graph.createValidChildName(node_name)
1261 self.
logger.debug(f
'> Scan {len(inputs)} inputs')
1262 for inputname, input_item
in inputs.items():
1266 input_type = input_item.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1268 self.
logger.error(f
'> Input type not found for graph input: {inputname}')
1272 mtlx_input = mtlx_graph.addInput(inputname, input_type)
1274 for meta
in metadata:
1275 if meta
in input_item:
1276 self.
logger.debug(f
'> Add extra interface attribute: {meta}, {input_item[meta]}')
1277 mtlx_input.setAttribute(meta, input_item[meta])
1280 if input_type ==
'filename':
1281 texture_index = input_item.get(
'texture',
None)
1282 if texture_index
is not None:
1283 gltf_textures = gltf_doc.get(
'textures',
None)
1284 gltf_images = gltf_doc.get(
'images',
None)
1285 if gltf_textures
and gltf_images:
1286 gltf_texture = gltf_textures[texture_index]
if texture_index < len(gltf_textures)
else None
1289 mtlx_input.setValueString(uri, input_type)
1292 input_value = input_item.get(
'value',
None)
1293 if input_value
is not None:
1295 if mtlx_value
is not None:
1296 mtlx_input.setValueString(mtlx_value)
1297 mtlx_input.setType(input_type)
1299 mtlx_input.setValueString(str(input_value))
1301 self.
logger.error(f
'> Interface input has no value specified: {inputname}')
1304 self.
logger.debug(f
'> Scan {len(nodes)} nodes')
1306 node_name = node.get(KHR_TEXTURE_PROCEDURALS_NAME,
None)
1307 node_type = node.get(
'nodetype',
None)
1308 output_type = node.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1309 node_outputs = node.get(KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK, [])
1312 mtlx_node = mtlx_graph.addChildOfCategory(node_type, node_name)
1315 if len(node_outputs) > 1:
1316 output_type = MULTI_OUTPUT_TYPE_STRING
1320 mtlx_node.setType(output_type)
1322 self.
logger.error(f
'> No output type specified for node: {node_name}')
1326 for key, value
in node.items():
1327 if key
not in [KHR_TEXTURE_PROCEDURALS_NAME,
'nodetype', KHR_TEXTURE_PROCEDURALS_TYPE, KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, KHR_TEXTURE_PROCEDURALS_OUTPUTS_BLOCK]:
1328 self.
logger.debug(f
'> Add extra node attribute: {key}, {value}')
1329 mtlx_node.setAttribute(key, value)
1332 node_inputs = node.get(KHR_TEXTURE_PROCEDURALS_INPUTS_BLOCK, {})
1333 for input_name, input_item
in node_inputs.items():
1335 input_name = MTLX_DEFAULT_INPUT_NAME
1336 input_type = input_item.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1339 mtlx_input = mtlx_node.addInput(input_name, input_type)
1342 if input_type ==
'filename':
1343 texture_index = input_item.get(
'texture',
None)
1344 if texture_index
is not None:
1345 gltf_textures = gltf_doc.get(
'textures',
None)
1346 gltf_images = gltf_doc.get(
'images',
None)
1347 if gltf_textures
and gltf_images:
1348 gltftexture = gltf_textures[texture_index]
if texture_index < len(gltf_textures)
else None
1351 mtlx_input.setValueString(uri)
1354 input_value = input_item.get(
'value',
None)
1355 if input_value
is not None:
1357 if mtlx_value
is not None:
1358 mtlx_input.setValueString(mtlx_value)
1359 mtlx_input.setType(input_type)
1361 self.
logger.error(f
'> Unsupported input type: {input_type}. Performing straight assignment.')
1362 mtlx_input.setValueString(str(input_value))
1369 if 'input' in input_item:
1371 input_key = input_item[
'input']
1372 if input_key
in inputs:
1373 connectable = inputs[input_key]
1374 mtlx_input.setAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1376 self.
logger.error(f
'Input key not found: {input_key}')
1379 elif 'output' in input_item:
1380 if 'node' not in input_item:
1381 connectable = outputs[input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT]]
if input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT] < len(outputs)
else None
1382 mtlx_input.setAttribute(
'output', connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1385 if 'node' in input_item:
1386 connectable = nodes[input_item[KHR_TEXTURE_PROCEDURALS_NODE]]
if input_item[KHR_TEXTURE_PROCEDURALS_NODE] < len(nodes)
else None
1387 mtlx_input.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1389 if 'output' in input_item:
1391 connectable = nodes[input_item[KHR_TEXTURE_PROCEDURALS_NODE]]
if input_item[KHR_TEXTURE_PROCEDURALS_NODE] < len(nodes)
else None
1399 mtlx_input.setAttribute(
'output', input_item[
'output'])
1400 self.
logger.debug(f
'Set output specifier on input: {mtlx_input.getNamePath()}. Value: {input_item[KHR_TEXTURE_PROCEDURALS_OUTPUT]}')
1403 for meta
in metadata:
1404 if meta
in input_item:
1405 self.
logger.debug(f
'> Add extra input attribute: {meta}, {input_item[meta]}')
1406 mtlx_input.setAttribute(meta, input_item[meta])
1409 if len(node_outputs) > 1:
1410 for output_name, output
in node_outputs.items():
1411 output_type = output.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1412 mtlxoutput = mtlx_node.addOutput(output_name, output_type)
1413 self.
logger.debug(f
'Add multioutput output {mtlxoutput.getNamePath()} of type {output_type} to node {node_name}')
1418 self.
logger.info(f
'> Scan {len(outputs)} procedural outputs')
1419 for output_name, output
in outputs.items():
1421 output_type = output.get(KHR_TEXTURE_PROCEDURALS_TYPE,
None)
1422 mtlx_graph_output = mtlx_graph.addOutput(output_name, output_type)
1427 if 'input' in output:
1428 connectable = inputs[output[KHR_TEXTURE_PROCEDURALS_INPUT]]
if output[KHR_TEXTURE_PROCEDURALS_INPUT] < len(inputs)
else None
1430 mtlx_graph_output.setAttribute(MTLX_INTERFACEINPUT_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1432 self.
logger.error(f
'Input not found: {output["input"]}, {inputs}')
1435 elif 'node' in output:
1436 connectable = nodes[output[KHR_TEXTURE_PROCEDURALS_NODE]]
if output[KHR_TEXTURE_PROCEDURALS_NODE] < len(nodes)
else None
1438 mtlx_graph_output.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, connectable[KHR_TEXTURE_PROCEDURALS_NAME])
1439 if 'output' in output:
1440 mtlx_graph_output.setAttribute(
'output', output[KHR_TEXTURE_PROCEDURALS_OUTPUT])
1442 self.
logger.error(f
'> Output node not found: {output["node"]}, {nodes}')
1445 for key, value
in output.items():
1446 if key
not in [KHR_TEXTURE_PROCEDURALS_NAME, KHR_TEXTURE_PROCEDURALS_TYPE,
'nodetype',
'node',
'output']:
1447 self.
logger.debug(f
'> Add extra graph output attribute: {key}. Value: {value}')
1448 mtlx_graph_output.setAttribute(key, value)