UP-Viagg-io/Viagg-io/Assets/Editor/mat-export.py

213 lines
12 KiB
Python
Raw Permalink Normal View History

2024-10-14 17:16:44 +02:00
import bpy
import json
import os
print ("V 0.1")
# Steintisch muesste texturen haben, hat aber keine...
# INFO: Steintisch.001 : traversing 'Texture Coordinate' (ShaderNodeTexCoord)
# WARNING: Steintisch.001 : recursion detected in 'Texture Coordinate-ShaderNodeTexCoord' ({'Texture Coordinate-ShaderNodeTexCoord'})
# INFO: Steintisch.001 : traversing 'Material Output' (ShaderNodeOutputMaterial)
# INFO: Steintisch.001 : traversing 'Displacement' (ShaderNodeDisplacement)
# WARNING: Steintisch.001 : recursion detected in 'Displacement-ShaderNodeDisplacement' ({'Displacement-ShaderNodeDisplacement', 'Texture Coordinate-ShaderNodeTexCoord', 'Material Output-ShaderNodeOutputMaterial'})
# INFO: Steintisch.001 : traversing 'Principled BSDF' (ShaderNodeBsdfPrincipled)
# WARNING: Steintisch.001 : recursion detected in 'Principled BSDF-ShaderNodeBsdfPrincipled' ({'Displacement-ShaderNodeDisplacement', 'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Material Output-ShaderNodeOutputMaterial'})
# INFO: Steintisch.001 : traversing 'RockDark001_6K' (ShaderNodeGroup)
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# WARNING: Steintisch.001 : recursion detected in 'RockDark001_6K-ShaderNodeGroup' ({'Principled BSDF-ShaderNodeBsdfPrincipled', 'Texture Coordinate-ShaderNodeTexCoord', 'Displacement-ShaderNodeDisplacement', 'RockDark001_6K-ShaderNodeGroup', 'Material Output-ShaderNodeOutputMaterial'})
# Prepare a dictionary to hold material and texture data
material_texture_data = {}
material_other_data = {}
node_name_mappings = {
"COLOR":"COLOR",
"NORMAL":"NORMAL",
"METALNESS":"METALNESS",
"ROUGHNESS":"ROUGHNESS"
}
link_to_socket_name_mappings = {
"Base Color": "COLOR",
"Alpha": "OPACITY",
"Metallic": "METALNESS",
"Roughness": "ROUGHNESS",
"Specular IOR Level": "SPECULAR",
"A": "COLOR"
}
link_from_node_name_mappings = {
"NRM": "NORMAL",
"DISP16": "DISPLACEMENT",
"AO": "AMBIENT_OCCLUSION",
"GLOSS": "GLOSS",
"TRANSMISSION": "ALPHA",
"BUMP": "BUMP",
"DISPLACEMENT": "DISPLACEMENT",
"A": "COLOR",
"COL": "COLOR",
"SSS": "SUBSURFACE_SCATTERING" # not sure about this one?
}
link_to_node_name_mappings = {
"Normal Map": "NORMAL",
"Displacement": "DISPLACEMENT",
"COLOR * AO": "COLOR"
}
def replace_starting_double_slash(s):
if s.startswith('//'):
return '' + s[2:] # Remove '//' at the beginning
return s # Return the original string if it doesn't start with '//'
# Function to recursively traverse the nodes and extract texture names and channels
def traverse_nodes(node, material_name, visited):
if f"{node.name}-{node.bl_idname}" in visited:
print (f"WARNING: {material_name} : recursion detected in '{node.name}-{node.bl_idname}' ({visited})")
# print (f"Testing {node.bl_rna} {node.bl_rna.identifier} {node.bl_rna.name} {node.bl_rna.name_property}")
# > Testing <bpy_struct, Struct("ShaderNodeBsdfPrincipled") at 0x106a37300> ShaderNodeBsdfPrincipled Principled BSDF <bpy_struct, StringProperty("name") at 0x106a16bb0>
# > bl_rna: '__doc__', '__module__', '__slots__', 'base', 'bl_rna', 'description', 'functions', 'identifier', 'input_template', 'is_registered_node_type', 'name', 'name_property', 'nested', 'output_template', 'properties', 'property_tags', 'rna_type', 'translation_context']
# material_data[material_name] = material_data[]
return
else:
# dir(node)
# > ['__doc__', '__module__', '__slots__', 'bl_description', 'bl_height_default', 'bl_height_max', 'bl_height_min', 'bl_icon', 'bl_idname', 'bl_label', 'bl_rna', 'bl_static_type', 'bl_width_default', 'bl_width_max', 'bl_width_min', 'color', 'color_mapping', 'dimensions', 'draw_buttons', 'draw_buttons_ext', 'extension', 'height', 'hide', 'image', 'image_user', 'input_template', 'inputs', 'internal_links', 'interpolation', 'is_registered_node_type', 'label', 'location', 'mute', 'name', 'output_template', 'outputs', 'parent', 'poll', 'poll_instance', 'projection', 'projection_blend', 'rna_type', 'select', 'show_options', 'show_preview', 'show_texture', 'socket_value_update', 'texture_mapping', 'type', 'update', 'use_custom_color', 'width']
print (f"INFO: {material_name} : traversing '{node.name}' ({node.bl_idname}, {node.type})")
# print (dir(node))
# break
visited.add(f"{node.name}-{node.bl_idname}")
# Check if the node is an image texture node
if node.type == 'TEX_IMAGE' and node.image:
if node.image.packed_file: # Check if the image is packed
image_name = node.image.name # Just the name if packed
print (f"ERROR: {material_name} uses packed image {image_name}")
else: # It's a linked image
image_name = replace_starting_double_slash(node.image.filepath) # Get the full path
# Identify the channel based on the node's connections
channels = []
comments = ""
for output in node.outputs:
for link in output.links:
# connected_node = link.from_node
# You can customize this to capture specific channel types if needed
if (node.name in node_name_mappings.keys()):
channels.append(node_name_mappings[node.name])
elif (link.to_socket.name in link_to_socket_name_mappings.keys()):
channels.append(link_to_socket_name_mappings[link.to_socket.name])
elif (link.from_node.name in link_from_node_name_mappings.keys()):
channels.append(link_from_node_name_mappings[link.from_node.name])
elif (link.to_node.name in link_to_node_name_mappings.keys()):
channels.append(link_to_node_name_mappings[link.to_node.name])
else:
channels.append("UNDEFINED")
comments += f"| output.name: {output.name}, node.name: {node.name}, \
link.from_socket.name: {link.from_socket.name}, \
link.to_socket.name: {link.to_socket.name}, \
link.from_node.name: {link.from_node.name}, \
link.to_node.name: {link.to_node.name} |"
if len(channels) == 1:
if (image_name!= ""):
# Append the image name and channels to the material's entry
if material_name not in material_texture_data:
material_texture_data[material_name] = []
material_texture_data[material_name].append({
'image_name': image_name,
'channel': channels[0],
'comment': comments
})
print (f"INFO: {material_name} : node {node.name} has: {image_name} on {channels[0]}")
elif len(channels) > 1:
print (f"WARNING: {material_name} : node {node.name} has multiple or no channels: {channels}")
# Inside the traverse_nodes function, where you have the TODO comment
elif (node.type == 'GROUP'):
print(f"INFO: {material_name} : traversing group node '{node.name}' ({node.bl_idname})")
# traverse_nodes(node.node_tree, material_name, visited)
# Access the node tree of the group
node_group = node.node_tree
if node_group: # Check if the node group exists
# Iterate over the output nodes of the group
output_nodes = [n for n in node_group.nodes if n.type == 'OUTPUT_GROUP']
for output_node in output_nodes:
# Recursively traverse from the output node
traverse_nodes(output_node, material_name, visited)
# Optionally, you might also want to traverse all nodes in the group
for group_node in node_group.nodes:
if group_node not in visited: # Avoid recursion within the group
traverse_nodes(group_node, material_name, visited)
else:
print(f"WARNING: {material_name} : group node '{node.name}' has no node tree")
elif node.type == 'BSDF_PRINCIPLED':
# Access the roughness value
roughness_value = node.inputs['Roughness'].default_value
print(f"Material: {mat.name}, Roughness: {roughness_value}")
if material_name not in material_other_data:
material_other_data[material_name] = {}
material_other_data[material_name].update({
'roughness': roughness_value
})
else:
# node is not an image
pass
# Recursively check for other nodes connected to the current node
for output in node.outputs:
for link in output.links:
connected_node = link.from_node
traverse_nodes(connected_node, material_name, visited)
# Loop through all materials in the current Blender file
for mat in bpy.data.materials:
if mat.use_nodes:
visited_nodes = set() # Set to track visited nodes
for node in mat.node_tree.nodes: # Iterate over nodes
traverse_nodes(node, mat.name, visited_nodes)
else:
print (f"{mat} is not using nodes")
# Get the current Blender file's name without extension
blend_file_name = bpy.data.filepath
if blend_file_name:
base_name = os.path.splitext(os.path.basename(blend_file_name))[0]
else:
base_name = "materials_data"
# convert the data to the output format
material_data_output = []
for material_key, material_value in material_texture_data.items():
material_data_dict = {
"materialName": material_key,
"textureInfos": material_value
}
if (material_key in material_other_data.keys()):
print (f"Updating {material_data_dict} with {material_other_data[material_key]}")
material_data_dict.update(material_other_data[material_key])
# for key, value in material_other_data[material_key].items():
# material_data_dict[key](material_other_data[material_key])
material_data_output.append( material_data_dict )
# Set the output file path
output_file_path = os.path.join(os.path.dirname(blend_file_name), f"{base_name}_materials_data.json")
# Save the material data to a JSON file
with open(output_file_path, 'w') as f:
json.dump({"materials":material_data_output}, f, indent=4)
print(f"Material data saved to {output_file_path}")