Advertisement
Guest User

load_shader_map.py

a guest
May 2nd, 2021
48
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #import all libraries including re needed for regex matching
  2. #import Path library to search recursively for files
  3. import bpy, re
  4. import glob
  5. from pathlib import Path
  6.  
  7.  
  8. import os
  9.  
  10.  
  11. #import from other files required functions and classes
  12. import sys
  13.  
  14. sys.path.append(".")
  15. #import classes
  16. from save_shader_map import SHADER_PRESETS_UL_items, ShowMessageOperator
  17. #import functions
  18. from save_shader_map import get_preferences, get_selected_folder_presets, json_to_nodes_dict
  19.  
  20.  
  21. from mathutils import (Vector, Euler, Color)
  22.  
  23. #define globals
  24. SHADER_EDITOR = "ShaderNodeTree"
  25. COMPOSITOR_EDITOR = "CompositorNodeTree"
  26. ANIMATION_NODE_EDITOR = "an_AnimationNodeTree"
  27.  
  28. # Special handled node types
  29. NOT_NEEDED_NODE_TYPES = [
  30.     "NodeReroute"
  31. ]
  32.  
  33. # Not to handle atts for these node types
  34. NOT_TO_HANDLE_ATTRS_NODES = [
  35.     # Need to handle CompositorNodeImage
  36.     # "CompositorNodeImage"
  37. ]
  38.  
  39.  
  40.  
  41. #define all user input properties
  42. class PathProperties(bpy.types.PropertyGroup):
  43.     props_txt_path: bpy.props.StringProperty(name="Select PropsTxt File", description="Select a props.txt file", subtype="FILE_PATH")
  44.     skin_map_path: bpy.props.StringProperty(name="Select Skin Map File", description="Select a skin map image file", subtype="FILE_PATH")
  45.     material_folder_path: bpy.props.StringProperty(name="Select Materials Folder", description="Select a Materials folder", subtype="DIR_PATH")
  46.     export_folder_path: bpy.props.StringProperty(name="Select Exported Game Folder", description="Select a Game folder", subtype="DIR_PATH")
  47.     is_replace_nodes: bpy.props.BoolProperty(name="Replace Existing Shader Maps", default= True)
  48.     # shader_type_enum: bpy.props.EnumProperty(
  49.     #     name = "Shader Map Type",
  50.     #     description = "Dropdown List of all the types of Shader Maps",
  51.     #     items =
  52.     #     [
  53.     #         ("DBDRomanNoodlesYanima" , "DBD Roman Noodles & YanimaDBD Shader Maps", ""),
  54.     #         ("DBDFrutto" , "DBD Frutto Shader Maps", ""),
  55.     #         ("HSHSTico" , "HSHS Tico Shader Maps", "")
  56.     #     ]
  57.        
  58.     # )
  59.     is_add_img_textures: bpy.props.BoolProperty(name="Add Image Textures", default= True)
  60.     is_delete_unused_img_texture_nodes: bpy.props.BoolProperty(name="Delete Unused Image Texture Nodes", default= True)
  61.     # is_material_skin: bpy.props.BoolProperty(name="Add Skin Related Nodes", default= False)
  62.     # is_add_height_map: bpy.props.BoolProperty(name="Add Height Map Skin Texture", default= False)
  63.  
  64.  
  65. #code for drawing main panel in the 3D View
  66. class LOADUESHADERSCRIPT_PT_main_panel(bpy.types.Panel):
  67.     bl_label = "Load UE Shaders"
  68.     bl_idname = "LOADUESHADERSCRIPT_PT_main_panel"
  69.     bl_space_type = 'VIEW_3D'
  70.     bl_region_type = 'UI'
  71.     bl_category = "UE Shaders"
  72.    
  73.     def draw(self, context):
  74.         layout = self.layout
  75.        
  76.         #store active/selected scene to variable
  77.         scene = context.scene
  78.         #allow access to user inputted properties through pointer
  79.         #to properties
  80.         pathtool = scene.path_tool
  81.  
  82.         #set isOverridePackage to override __package__ as it does
  83.         #not work for imported functions
  84.         isOverridePackage = True
  85.         preferences = get_preferences(isOverridePackage)
  86.  
  87.         #make folder section
  88.         box = layout.box()
  89.         row = box.row()
  90.         row.label(text="Folders")
  91.         row = box.row()
  92.         left = row.column()
  93.         left.alignment = "RIGHT"
  94.         left.prop(preferences, 'folders', expand=False)
  95.  
  96.        
  97.  
  98.         selected_folders_presets = get_selected_folder_presets(isOverridePackage)
  99.  
  100.         #create the list of current presets
  101.         layout.template_list("SHADER_PRESETS_UL_items", "", selected_folders_presets,
  102.                                "presets", selected_folders_presets, "preset_index", rows=5)
  103.  
  104.         #option to replace or keep existing nodes in materials
  105.         layout.prop(pathtool, "is_replace_nodes")
  106.  
  107.         #option to delete image texture nodes which have not had a texture
  108.         #loaded into them
  109.         layout.prop(pathtool, "is_delete_unused_img_texture_nodes")
  110.        
  111.         #Create a box for all related inputs and operators
  112.         #for adding the shader maps one by one to
  113.         #selected material
  114.         box = layout.box()
  115.        
  116.         #--------------draw user input boxes
  117.         #create box for all related boxes adding shader map to selected material
  118.         box.label(text = "ADD SHADER MAP TO SELECTED MATERIAL (ONE MATERIAL)",)
  119.         box.label(text = "Select a mesh and a material and add a shader map to the selected material")
  120.         box.prop(pathtool, "props_txt_path")
  121.         box.prop(pathtool, "export_folder_path")
  122.         box.prop(pathtool, "skin_map_path")
  123.         box.operator("loadueshaderscript.addbasic_operator")
  124.                
  125.         #Create a box for adding shader maps to all materials
  126.         #to the selected mesh with all
  127.         #related inputs and operators
  128.         box = layout.box()
  129.         box.label(text = "ADD SHADER MAP TO ALL MATERIALS ON SELECTED MESHES (ALL MATERIALS)")
  130.         box.label(text = "Select multiple meshes and add shader maps to all the materials on the selected meshes")
  131.         box.prop(pathtool, "material_folder_path")
  132.         box.prop(pathtool, "export_folder_path")
  133.         box.prop(pathtool, "skin_map_path")
  134.         box.operator("loadueshaderscript.addbasicall_operator" )
  135.        
  136.        
  137.  
  138.  
  139. class LOADUESHADERSCRIPT_OT_add_basic(bpy.types.Operator):
  140.     bl_label = "Add ONE Shader Map"
  141.     bl_idname = "loadueshaderscript.addbasic_operator"
  142.     def execute(self, context):
  143.         scene = context.scene
  144.         #allow access to user inputted properties through pointer
  145.         #to properties
  146.         pathtool = scene.path_tool
  147.        
  148.         #find the selected material and
  149.         #create a basic shader on it
  150.         selected_mat = bpy.context.active_object.active_material
  151.        
  152.         create_one_shader_map(context, selected_mat, pathtool.props_txt_path, pathtool)
  153.        
  154.         return {"FINISHED"}
  155.  
  156.  
  157. class LOADUESHADERSCRIPT_OT_add_basic_all(bpy.types.Operator):
  158.     bl_label = "Add ALL Shader Maps"
  159.     bl_idname = "loadueshaderscript.addbasicall_operator"
  160.     def execute(self, context):
  161.         scene = context.scene
  162.         #allow access to user inputted properties through pointer
  163.         #to properties
  164.         pathtool = scene.path_tool
  165.        
  166.         create_basic_all_shader_maps(context, pathtool)
  167.        
  168.    
  169.         return {"FINISHED"}
  170.  
  171.  
  172.  
  173. #create all basic shader maps function
  174. #just runs the create one basic shader map function
  175. #for all selected objects and all materials
  176. def create_basic_all_shader_maps(context, pathtool):
  177.     #if the folder path is a relative path
  178.     #turn it into an absolute one
  179.     #as relative paths cause problems
  180.     #when trying to load an image
  181.     #paths already absolute not affected
  182.     abs_mat_folder_path =  bpy.path.abspath(pathtool.material_folder_path)
  183.    
  184.    
  185.     #To get a specific material you have to use:
  186.     #bpy.context.selected_objects[0].data.materials[0]
  187.    
  188.     #this makes a list of all selected objects (can be multiple)
  189.     objects_list = bpy.context.selected_objects
  190.    
  191.    
  192.     #go through each selected object
  193.     #and in every selected object
  194.     #go through all of the selected object's materials
  195.     for active_obj in objects_list:
  196.        
  197.         #shade smooth on the all selected meshes
  198.         #inside loop
  199.         #must use context.object.data
  200.         #as part of bpy.ops
  201.         #as bpy.ops.object.shade_smooth() as part of bpy.ops
  202.         #not bpy.data
  203.         mesh = active_obj.data
  204.         for f in mesh.polygons:
  205.             f.use_smooth = True
  206.        
  207.         #fetch all materials of the current active_object in a list
  208.         active_obj_materials_list = active_obj.data.materials[:]
  209.    
  210.         #create a shader map for each material in the materials list
  211.         #in a loop
  212.         for material in active_obj_materials_list:
  213.             #make sure to use nodes before anything else
  214.             #this is because if you don't have use nodes
  215.             #enabled, the material and material.name will not work properly
  216.             material.use_nodes = True
  217.            
  218.             #returns a generator object with the matching
  219.             #absolute path to the props txt file
  220.             #rglob is a globbing function (match and return a pattern)
  221.             #and Path is a path type object, used for easy manipulation of paths
  222.            
  223.             #we use rglob and search for the
  224.             #props.txt file because the file we are looking for
  225.             #might be in a subdirectory such as Outfit01
  226.             #and the user might have selected the material folder path
  227.             #C:\Nyan\Dwight Recolor\Game\Characters\Slashers\Nurse\Materials\
  228.             #instead of C:\Nyan\Dwight Recolor\Game\Characters\Slashers\Nurse\Materials\Outfit01
  229.             #this allows for extra redundancy
  230.             #so the props.txt file can be either in the current directory, or its subdirectories
  231.             gen_obj_match = Path(abs_mat_folder_path).rglob(material.name + ".props.txt")
  232.            
  233.             props_txt_path = get_value_in_gen_obj(gen_obj_match)
  234.            
  235.             #debug
  236.             #print("props_txt_path:", props_txt_path)
  237.             #if can't find the propstxt file in the material folder do a recursive glob search
  238.             #in the exported Game folder which costs more time since many folders
  239.             #because it might be somewhere else like this instead
  240.             #\Game\Characters\Campers\CommonAcc\Materials\Top\MI_CMMHair019_TAA.props.txt
  241.             if props_txt_path == "":
  242.                 abs_export_folder_path = bpy.path.abspath(pathtool.export_folder_path)
  243.                 gen_obj_match = Path(abs_export_folder_path).rglob(material.name + ".props.txt")
  244.                 #get the new props_txt_path in the new generator object
  245.                 props_txt_path = get_value_in_gen_obj(gen_obj_match)
  246.                 #debug
  247.                 #print("refind props_txt_path:", props_txt_path)
  248.                
  249.            
  250.            
  251.             #not needed any more
  252.             #get the current material's name to concatenate
  253.             #a string the path to the props.txt file
  254.             #props_txt_path = abs_mat_folder_path + material.name + ".props.txt"
  255.    
  256.             create_one_shader_map(context, material, props_txt_path, pathtool)
  257.            
  258.     return {"FINISHED"}  
  259.  
  260.  
  261. def get_value_in_gen_obj(gen_obj_match):
  262.     #set default values to be used in for loop
  263.     props_txt_path = ""
  264.     match = 0
  265.    
  266.     #only way to access values inside a generator object is
  267.     #to use a for loop
  268.     for file_path in gen_obj_match:
  269.         match = match + 1
  270.         props_txt_path = file_path
  271.    
  272.     #should only be one match from rglob
  273.     #because we are matching one specific file
  274.     #so if more matches then one print an error
  275.     if match > 1:
  276.         print("Error: There was more than one match for the props_txt_path for rglob")
  277.    
  278.     return props_txt_path
  279.  
  280.  
  281. def create_one_shader_map(context, material, props_txt_path, pathtool):
  282.     #makes sure use_nodes is turned on
  283.     #for just imported meshes from the psk importer
  284.     material.use_nodes = True
  285.  
  286.     #this makes a list of all selected objects (can be multiple)
  287.     active_obj = bpy.context.active_object
  288.    
  289.     #shade smooth on the active object
  290.     #may already be shaded smooth if coming from the
  291.     #create_all_shader_maps
  292.     #but this is just in case the user only runs create_one_shader_map
  293.     mesh = active_obj.data
  294.     for f in mesh.polygons:
  295.         f.use_smooth = True
  296.  
  297.  
  298.  
  299.     #convert windows path to string
  300.     props_txt_path = str(props_txt_path)
  301.  
  302.     #if the folder path is a relative path
  303.     #turn it into an absolute one
  304.     #as relative paths cause problems
  305.     #when trying to load an image
  306.     #absolute paths will stay as absolute paths
  307.     abs_props_txt_path =  bpy.path.abspath(props_txt_path)
  308.  
  309.     #if bool is checked delete all nodes to create a clean slate
  310.     #for the new node map to be loaded
  311.     if (pathtool.is_replace_nodes):
  312.         tree = material.node_tree
  313.         clear_nodes(tree)
  314.         clear_links(tree)
  315.    
  316.     load_preset(context, material, abs_props_txt_path, pathtool)
  317.     #choose which shader map type to be be using
  318.     # if (pathtool.shader_type_enum == "DBDRomanNoodlesYanima"):
  319.     #     roman_noodles_shader_map(material, abs_props_txt_path, pathtool)
  320.        
  321.     # elif (pathtool.shader_type_enum == "DBDFrutto"):
  322.     #     frutto_shader_map(material, abs_props_txt_path, pathtool)
  323.        
  324.     # elif (pathtool.shader_type_enum == "HSHSTico"):
  325.     #     tico_shader_map(material, abs_props_txt_path, pathtool)
  326.  
  327.  
  328. def clear_nodes(tree):
  329.     nodes = tree.nodes
  330.     nodes.clear()
  331.  
  332.  
  333. def clear_links(tree):
  334.     links = tree.links
  335.     links.clear()
  336.  
  337.  
  338. def load_preset(context, material, props_txt_path, pathtool):
  339.     area = context.area
  340.     #editor_type = area.ui_type
  341.  
  342.     JSON = get_json_from_selected_preset()
  343.     if JSON == {'FINISHED'}:
  344.         return JSON
  345.     nodes_dict = json_to_nodes_dict(JSON)
  346.     #debug print the nodes dictionary
  347.     #look at this in notepad++ with JSON Sort plugin
  348.     #to see it clearly
  349.     #print("nodes_dict:",nodes_dict)
  350.     # if nodes_dict["editor_type"] != editor_type:
  351.     #     bpy.ops.nodekit.show_message(
  352.     #         message="Selected preset can not be restored to this node editor.")
  353.     #     return {'FINISHED'}
  354.     if nodes_dict["editor_type"] == SHADER_EDITOR:
  355.         if (bpy.context.object is not None and bpy.context.object.type != "MESH" and bpy.context.object.type != "LIGHT") or bpy.context.object is None:
  356.             bpy.ops.nodekit.show_message(
  357.                 message="Selected object canot be restored, please choose a Mesh or a Lamp to restore.")
  358.             return {'FINISHED'}
  359.         # if "shader_type" in nodes_dict:
  360.         #     shader_type_value = nodes_dict["shader_type"]
  361.         #     area.spaces[0].shader_type = shader_type_value
  362.         # Branch for shader type: Object or World
  363.         # shader_type = area.spaces[0].shader_type
  364.         shader_type = nodes_dict["shader_type"]
  365.         if bpy.context.object.type == "LIGHT" and shader_type == "OBJECT":
  366.             light = bpy.data.lights[bpy.context.object.name]
  367.             light.use_nodes = True
  368.             node_tree = light.node_tree
  369.         elif bpy.context.object.type == "LIGHT" and shader_type == "WORLD":
  370.             world = get_active_world()
  371.             node_tree = world.node_tree
  372.         elif shader_type == "OBJECT":
  373.             if bpy.context.object is None:
  374.                 bpy.ops.nodekit.show_message(
  375.                     message="Please choose an object in View 3D to restore.")
  376.                 return {'FINISHED'}
  377.             #mat = add_material_to_active_object()
  378.             mat = material
  379.             node_tree = mat.node_tree
  380.         elif shader_type == "WORLD":
  381.             world = get_active_world()
  382.             node_tree = world.node_tree
  383.         nodes = dict_to_nodes(nodes_dict["nodes_list"], node_tree)
  384.         list_to_links(nodes_dict["links_list"], node_tree, nodes)
  385.         dict_to_textures(nodes_dict["img_textures_list"], material, node_tree, props_txt_path, pathtool)
  386.     else:
  387.          bpy.ops.nodekit.show_message(
  388.                     message="Only Shader Editor Restores are supported currently not Compositor editor restores.")
  389.  
  390.  
  391.     # elif nodes_dict["editor_type"] == COMPOSITOR_EDITOR:
  392.     #     bpy.context.scene.use_nodes = True
  393.     #     node_tree = bpy.context.scene.node_tree
  394.     #     if clear:
  395.     #         clear_nodes(node_tree)
  396.     #         clear_links(node_tree)
  397.     #     nodes = dict_to_nodes(nodes_dict["nodes_list"], node_tree)
  398.     #     list_to_links(nodes_dict["links_list"], node_tree, nodes)
  399.     # elif nodes_dict["editor_type"] == ANIMATION_NODE_EDITOR:
  400.     #     bpy.context.scene.use_nodes = True
  401.     #     area = context.area
  402.     #     node_editor = area
  403.     #     node_tree = node_editor.spaces[0].node_tree
  404.     #     if not node_tree:
  405.     #         bpy.ops.node.new_node_tree()
  406.     #         node_tree = node_editor.spaces[0].node_tree
  407.     #     if clear:
  408.     #         clear_nodes(node_tree)
  409.     #         clear_links(node_tree)
  410.     #     nodes = dict_to_nodes_for_animation_nodes(
  411.     #         nodes_dict["nodes_list"], node_tree)
  412.     #     list_to_links_for_animation_nodes(
  413.     #         nodes_dict["links_list"], node_tree, nodes)
  414.     # if from_10_last:
  415.     #     pass
  416.     # elif from_10_most:
  417.     #     pass
  418.     # else:
  419.     #     # Add preset to 10 last used presets
  420.     #     folder_name = get_selected_folder_name()
  421.     #     preset_name = get_selected_preset_name()
  422.     #     add_preset_to_10_last(folder_name, preset_name)
  423.     #     # Update 10 most used presets
  424.     #     update_selected_preset_used_count()
  425.     #     update_10_most_used_presets()
  426.  
  427.  
  428. def get_active_world():
  429.     world = bpy.context.scene.world
  430.     if world:
  431.         world.use_nodes = True
  432.         return world
  433.     new_world = bpy.data.worlds.new(name="World")
  434.     new_world.use_nodes = True
  435.     bpy.context.scene.world = new_world
  436.     return new_world
  437.  
  438.  
  439. def add_material_to_active_object():
  440.     obj_data = bpy.context.object.data
  441.     obj = bpy.context.object
  442.     if obj.active_material:
  443.         obj.active_material.use_nodes = True
  444.         return obj.active_material
  445.     new_mat = bpy.data.materials.new("Material")
  446.     new_mat.use_nodes = True
  447.     obj_data.materials.append(new_mat)
  448.     obj.active_material = new_mat
  449.     return new_mat
  450.  
  451. def get_json_from_selected_preset():
  452.     isOverridePackage = True
  453.     selected_folder_presets = get_selected_folder_presets(isOverridePackage)
  454.     index = selected_folder_presets.preset_index
  455.     if index < 0:
  456.         bpy.ops.nodekit.show_message(
  457.             message="Please choose a nodes preset to restore.")
  458.         return {'FINISHED'}
  459.     JSON = selected_folder_presets.presets[index].content
  460.     return JSON
  461.  
  462. #--------------------DICTIONARY CONVERT BACK TO NODES SHADER MAP
  463.  
  464.  
  465. def dict_to_nodes(nodes_list, tree):
  466.     nodes = tree.nodes
  467.     ret_nodes = []
  468.     for node in nodes_list:
  469.         # Fixed: in input_dict_to_socket_value
  470.         # input.default_value[0] = value[0]
  471.         # TypeError: ‘float’ object does not support item assignment
  472.         if node["node_name"] in NOT_NEEDED_NODE_TYPES:
  473.             new_node = nodes.new(type=node["node_name"])
  474.             ret_nodes.append(new_node)
  475.             new_node.width = node["width"]
  476.             new_node.width_hidden = node["width_hidden"]
  477.             new_node.height = node["height"]
  478.             if node["parent"] != "None":
  479.                 parent = nodes[node["parent"]]
  480.                 new_node.parent = parent
  481.             new_node.location.x = node["x"]
  482.             new_node.location.y = node["y"]
  483.             continue
  484.         new_node = nodes.new(type=node["node_name"])
  485.         ret_nodes.append(new_node)
  486.         new_node.width = node["width"]
  487.         new_node.width_hidden = node["width_hidden"]
  488.         new_node.height = node["height"]
  489.         if node["parent"] != "None":
  490.             parent = nodes[node["parent"]]
  491.             new_node.parent = parent
  492.         new_node.location.x = node["x"]
  493.         new_node.location.y = node["y"]
  494.         # Special handlling ShaderNodeGroup
  495.         if node["node_name"] == "ShaderNodeGroup":
  496.             dict_to_nodes_handle_shader_node_group(new_node, node)
  497.         # if node["node_name"] == "CompositorNodeGroup":
  498.         #     dict_to_nodes_handle_compositor_node_group(new_node, node)
  499.         # if node["node_name"] == "CompositorNodeOutputFile":
  500.         #     dict_to_nodes_handle_compositor_node_output_file(new_node, node)
  501.         #     new_node.format.file_format = node["file_format"]
  502.         if node["node_name"] in NOT_TO_HANDLE_ATTRS_NODES:
  503.             continue
  504.         for attr_dict in node["attrs"]:
  505.             dict_to_attr(new_node, attr_dict)
  506.         # Repeat one more time to make sure UI updates right
  507.         for attr_dict in node["attrs"]:
  508.             dict_to_attr(new_node, attr_dict, repeated=True)
  509.         inputs = new_node.inputs
  510.         for index, input_dict in enumerate(node["inputs"]):
  511.             # Get the right input socket by name
  512.             input = get_input_by_name(inputs, input_dict["name"], index)
  513.             if input is not None:
  514.                 input_dict_to_socket_value(input, input_dict)
  515.             #input_dict_to_socket_value(inputs[index], input_dict)
  516.         outputs = new_node.outputs
  517.         for index, output_dict in enumerate(node["outputs"]):
  518.             # Get the right ouput socket by name
  519.             output = get_output_by_name(outputs, output_dict["name"], index)
  520.             if output is not None:
  521.                 output_dict_to_socket_value(output, output_dict)
  522.             #output_dict_to_socket_value(outputs[index], output_dict)
  523.     return ret_nodes
  524.  
  525. def dict_to_nodes_handle_shader_node_group(new_node, node_dict):
  526.     node_tree_of_group = group = bpy.data.node_groups.new(type="ShaderNodeTree",
  527.                                                           name=node_dict["node_tree"]["name"])
  528.     for input in node_dict["inputs"]:
  529.         new_node.inputs.new(input["type_name"], input["name"])
  530.         node_tree_of_group.inputs.new(input["type_name"], input["name"])
  531.     for output in node_dict["outputs"]:
  532.         new_node.outputs.new(output["type_name"], output["name"])
  533.         node_tree_of_group.outputs.new(output["type_name"], output["name"])
  534.     nodes = dict_to_nodes(node_dict["node_tree"]
  535.                           ["nodes_list"], node_tree_of_group)
  536.     list_to_links(node_dict["node_tree"]["links_list"],
  537.                   node_tree_of_group, nodes)
  538.     interface_inputs = node_tree_of_group.inputs
  539.     inputs_list = node_dict["node_tree"]["interface_inputs"]
  540.     list_to_interface_inputs(interface_inputs, inputs_list)
  541.     new_node.node_tree = node_tree_of_group
  542.  
  543.  
  544. def list_to_links(links_list, tree, nodes):
  545.     links = tree.links
  546.     for link in links_list:
  547.         from_node_index = link["from_node_index"]
  548.         from_node = nodes[from_node_index]
  549.         from_socket_index = link["from_socket_index"]
  550.         #from_socket = from_node.outputs[from_socket_index]
  551.         from_socket = get_output_by_name(
  552.             from_node.outputs, link["from_socket_name"], from_socket_index)
  553.         to_node_index = link["to_node_index"]
  554.         to_node = nodes[to_node_index]
  555.         to_socket_index = link["to_socket_index"]
  556.         #to_socket = to_node.inputs[to_socket_index]
  557.         to_socket = get_input_by_name(
  558.             to_node.inputs, link["to_socket_name"], to_socket_index)
  559.         if to_socket == None or from_socket == None:
  560.             continue
  561.         links.new(from_socket, to_socket)
  562.  
  563.  
  564. def dict_to_textures(img_textures_list, material, node_tree, props_txt_path, pathtool):
  565.     print("\nprops_txt_path", props_txt_path)
  566.    
  567.     #open the propstxt file for the material and find the
  568.     #texture locations from it
  569.     #with just means open and close file
  570.     with open(props_txt_path, 'r') as f:
  571.         #read entire file to one string
  572.         data = f.read()
  573.         #find all matches through regex to the string Texture2D' with capture group
  574.         #any character zero to unlimited times and ending with '
  575.         #also store capture groups into a list variable
  576.         match_list = re.findall("Texture2D\'(.*)\.", data)
  577.  
  578.  
  579.     #---------------------add image texture nodes
  580.     if pathtool.is_add_img_textures:
  581.  
  582.         not_delete_img_texture_node_list = []
  583.         #use loop to go through all locations
  584.         #specified in props.txt file
  585.         #and create image texture nodes +
  586.         #load all images for image textures
  587.         for tex_location in match_list:
  588.            
  589.             #fetch last 6 characters in path which will tell you what
  590.             #the current texture is in charge of e.g slice _BC off
  591.             #/Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC
  592.             #longest id is _ORM or _BDE 4 characters
  593.             tex_id = tex_location[-6:]
  594.            
  595.             #if the folder path is a relative path
  596.             #turn it into an absolute one
  597.             #as relative paths cause problems
  598.             #when trying to load an image
  599.             abs_export_folder_path = bpy.path.abspath(pathtool.export_folder_path)
  600.            
  601.             # Returns user specified export game folder path
  602.             # with first character removed
  603.             # reason why is there would be double up of \/ when
  604.             #concatenating strings
  605.             user_tex_folder = abs_export_folder_path[:-1]
  606.            
  607.             #replace forward slash with backslash reason why is
  608.             # when concatenating complete path looks like this
  609.             #if no replace path looks like e.g. C:\Nyan\Dwight Recolor\Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC
  610.             #which is weird
  611.             #backslash is used to escape backslash character
  612.             tex_location = tex_location.replace("/","\\")
  613.            
  614.             #if the user selects the game folder instead of the
  615.             #parent folder, the first 5 characters of
  616.             #the user input box: user_tex_folder will be "Game"
  617.             #so we remove "Game\" from the tex_location
  618.             #to avoid a double up
  619.             #this is extra redundancy so if the
  620.             #user chooses either the Game folder or
  621.             #the parent folder of the Game folder
  622.             #both options will work
  623.             if user_tex_folder[-4:] == "Game":
  624.                 #backslash is used to escape backslash character
  625.                 tex_location = tex_location.replace("\\Game","")
  626.      
  627.             #must string concatenate the user specified texture location path to
  628.             #the texture location
  629.             #as the tex_location will only be
  630.             #e.g /Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC
  631.             #this does not provide a complete path to where the user exported
  632.             #the texture
  633.             #we need e.g. C:\Nyan\Dwight Recolor\Game\Characters
  634.             #\Slashers\Bear\Textures\Outfit01\T_BEHead01_BC
  635.             complete_path = user_tex_folder + tex_location + ".tga"
  636.  
  637.             #If the texture is listed in the
  638.             #props.txt file and it is one of the
  639.             #image textures we are interested in we will
  640.             #load the corresponding image
  641.            
  642.             #this for loop will load all image textures
  643.             #via reading the img_textures_list
  644.             #recorded in the node_dict when the dictionary is saved
  645.             for textures in img_textures_list:
  646.                 suffix = textures["suffix"]
  647.                 node_name = textures["suffix_node"]
  648.                 #check what the last two/three characters are of the id
  649.                 #and look for the specific ids we are interested in
  650.                 #identifier
  651.                 #tex_id is from the props txt file comparing against
  652.                 #suffix which is what is recorded from the node_dict
  653.                 if tex_id.endswith(suffix):
  654.                     #looks like this normally
  655.                     #node_to_load = bpy.context.active_object.active_material.node_tree.nodes["Diffuse Node"]
  656.                     #node_to_load.image = bpy.data.images.load("C:\Seabrook\Dwight Recolor\Game\Characters\Campers\Dwight\Textures\Outfit01\T_DFHair01_BC.tga")
  657.  
  658.                     #use node_tree which is the current node tree
  659.                     #we're trying to texture
  660.                     #and node_name which is the Node Name in the Items panel in the shader editor
  661.                     #this will uniquely identify a single node
  662.                     node_to_load = node_tree.nodes[node_name]
  663.                     node_to_load.image = bpy.data.images.load(complete_path)
  664.  
  665.                     #special case if the node that was loaded was a transparency node _M
  666.                     #we need to set the material blend_method to alpha clip
  667.                     #and set the alpha threshold to 0 which looks best
  668.                     #with the least clipped
  669.                     if textures["texture"] == "transparency":
  670.                         material.blend_method = 'CLIP'
  671.                         material.alpha_threshold = 0
  672.  
  673.                     #if a image texture node has been loaded
  674.                     #and the option to delete image texture nodes who
  675.                     #have not had an image loaded to them is True
  676.                     #then we will add it to a whitelist
  677.                     #so it does not get deleted
  678.                     if pathtool.is_delete_unused_img_texture_nodes:
  679.                         not_delete_img_texture_node_list.append(node_to_load)
  680.        
  681.  
  682.         #check through the whole tree for image
  683.         #texture nodes and delete any
  684.         #that are not on the whitelist
  685.         if pathtool.is_delete_unused_img_texture_nodes:
  686.             #debug
  687.             #print("\nnot_delete_img_texture_node_list (the whitelist):", not_delete_img_texture_node_list)
  688.             nodes = node_tree.nodes
  689.             for node in nodes:
  690.                 #debug to find what the node.type is for
  691.                 #image texture nodes
  692.                 #print("node.type:", node.type)
  693.                 #if it's not in the whitelist which means
  694.                 #an image wasn't loaded to the node, delete the image texture node
  695.                 if node.type == "TEX_IMAGE" and not(node in not_delete_img_texture_node_list):
  696.                     nodes.remove(node)
  697.  
  698.            
  699.        
  700.  
  701.  
  702.        
  703.  
  704.  
  705. def get_input_by_name(inputs, name, index):
  706.     for i, input in enumerate(inputs):
  707.         if name == input.name and i == index:
  708.             return input
  709.     for i, input in enumerate(inputs):
  710.         if name == input.name:
  711.             return input
  712.     return None
  713.  
  714.  
  715. def input_dict_to_socket_value(input, input_dict):
  716.     t = input_dict["type_name"]
  717.     if t == "NodeSocketColor":
  718.         value = tuple(input_dict["value"])
  719.         input.default_value[0] = value[0]
  720.         input.default_value[1] = value[1]
  721.         input.default_value[2] = value[2]
  722.         input.default_value[3] = value[3]
  723.     elif t == "NodeSocketFloatFactor":
  724.         value = input_dict["value"]
  725.         input.default_value = value
  726.     elif t == "NodeSocketVector" or t == "NodeSocketVectorDirection" or \
  727.             t == "NodeSocketVectorEuler" or t == "NodeSocketVectorTranslation" or \
  728.             t == "NodeSocketVectorVelocity" or t == "NodeSocketVectorXYZ":
  729.         value = tuple(input_dict["value"])
  730.         input.default_value[0] = value[0]
  731.         input.default_value[1] = value[1]
  732.         input.default_value[2] = value[2]
  733.     elif t == "NodeSocketBool":
  734.         value = input_dict["value"]
  735.         if value == 0:
  736.             input.default_value = False
  737.         else:
  738.             input.default_value = True
  739.     elif t == "NodeSocketFloat":
  740.         input.default_value = input_dict["value"]
  741.     elif t == "NodeSocketFloatAngle":
  742.         input.default_value = input_dict["value"]
  743.     elif t == "NodeSocketFloatPercentage":
  744.         input.default_value = input_dict["value"]
  745.     elif t == "NodeSocketFloatTime":
  746.         input.default_value = input_dict["value"]
  747.     elif t == "NodeSocketFloatUnsigned":
  748.         input.default_value = input_dict["value"]
  749.     elif t == "NodeSocketInt":
  750.         input.default_value = input_dict["value"]
  751.     elif t == "NodeSocketIntFactor":
  752.         input.default_value = input_dict["value"]
  753.     elif t == "NodeSocketIntPercentage":
  754.         input.default_value = input_dict["value"]
  755.     elif t == "NodeSocketIntUnsigned":
  756.         input.default_value = input_dict["value"]
  757.     elif t == "NodeSocketString":
  758.         input.default_value = input_dict["value"]
  759.     elif t == "NodeSocketVector":
  760.         value = tuple(input_dict["value"])
  761.         input.default_value[0] = value[0]
  762.         input.default_value[1] = value[1]
  763.         input.default_value[2] = value[2]
  764.     elif t == "NodeSocketVectorAcceleration":
  765.         value = tuple(input_dict["value"])
  766.         input.default_value[0] = value[0]
  767.         input.default_value[1] = value[1]
  768.         input.default_value[2] = value[2]
  769.  
  770. def get_output_by_name(outputs, name, index):
  771.     for i, output in enumerate(outputs):
  772.         if name == output.name and i == index:
  773.             return output
  774.     for i, output in enumerate(outputs):
  775.         if name == output.name:
  776.             return output
  777.     return None
  778.  
  779. def list_to_interface_inputs(inputs, inputs_list):
  780.     for index, input in enumerate(inputs):
  781.         min_value = inputs_list[index]["min_value"]
  782.         max_value = inputs_list[index]["max_value"]
  783.         if min_value != "None":
  784.             input.min_value = min_value
  785.         if max_value != "None":
  786.             input.max_value = max_value
  787.  
  788. def output_dict_to_socket_value(output, output_dict):
  789.     input_dict_to_socket_value(output, output_dict)
  790.  
  791. def dict_to_attr(node, attr_dict, repeated=False):
  792.     t = attr_dict["type_name"]
  793.     name = attr_dict["attr_name"]
  794.     if t == "Not Handle Type":
  795.         return
  796.     if t == "NoneType":
  797.         v = None
  798.         return
  799.     v = attr_dict["value"]
  800.     if t == "bool":
  801.         if v == 0:
  802.             v = False
  803.         else:
  804.             v = True
  805.     if t == "tuple":
  806.         v = tuple(v)
  807.     if t == "Vector":
  808.         v = Vector(tuple(v))
  809.     if t == "Euler":
  810.         v = Euler(tuple(v), 'XYZ')
  811.     if t == "Text":
  812.         text_name = v
  813.         if text_name != "":
  814.             txt = bpy.data.texts.get(text_name)
  815.         v = txt
  816.     if t == "Image":
  817.         image = bpy.data.images.get(v)
  818.         if image is None:
  819.             try:
  820.                 bpy.ops.image.open(filepath=attr_dict["image_filepath"])
  821.             except Exception as e:
  822.                 print("[UE Shader]: file path of image attributes not found: ",
  823.                       attr_dict["image_filepath"])
  824.             filename = os.path.basename(attr_dict["image_filepath"])
  825.             image = bpy.data.images.get(filename)
  826.             if image is not None:
  827.                 image.source = attr_dict["image_source"]
  828.         if image is not None:
  829.             image.source = attr_dict["image_source"]
  830.         v = image
  831.     if t == "Object":
  832.         obj = bpy.data.objects.get(v)
  833.         v = obj
  834.     if t == "ImageUser":
  835.         set_values_for_ImageUser(node.image_user, v)
  836.         return
  837.     if t == "ParticleSystem":
  838.         obj_name = attr_dict["object_name"]
  839.         obj = bpy.data.objects[obj_name]
  840.         particle_system = obj.particle_systems.get(v)
  841.         ps = getattr(node, name)
  842.         v = particle_system
  843.     if t == "Color":
  844.         v = Color(tuple(v))
  845.     if t == "ColorRamp":
  846.         if repeated:
  847.             return
  848.         color_ramp_dict = v
  849.         color_ramp = node.color_ramp  # Currently all color ramp attr is via node.colorramp
  850.         color_ramp.color_mode = color_ramp_dict["color_mode"]
  851.         color_ramp.hue_interpolation = color_ramp_dict["hue_interpolation"]
  852.         color_ramp.interpolation = color_ramp_dict["interpolation"]
  853.         elements = color_ramp.elements
  854.         for e in elements:
  855.             print(e.position)
  856.         for index, color_ramp_element in enumerate(color_ramp_dict["elements"]):
  857.             if index == 0 or index == len(color_ramp_dict["elements"]) - 1:
  858.                 ele = elements[index]
  859.             else:
  860.                 ele = elements.new(color_ramp_element["position"])
  861.             ele.alpha = color_ramp_element["alpha"]
  862.             ele.color = tuple(color_ramp_element["color"])
  863.             ele.position = color_ramp_element["position"]
  864.     if t == "CurveMapping":
  865.         if repeated:
  866.            return
  867.         curve_mapping_dict = v
  868.         # Currently all curve mapping attr is via node.mapping
  869.         curve_mapping = node.mapping
  870.         set_values_for_CurveMapping(curve_mapping, curve_mapping_dict)
  871.         return
  872.     if t != "ColorRamp":
  873.         try:
  874.             setattr(node, name, v)
  875.         except Exception as e:
  876.             print("dict_to_attr() error:", str(e))
  877.  
  878. def set_values_for_CurveMapping(curve_mapping, value_dict):
  879.     def recreat_points(curve, total_points):
  880.         current_points = len(curve.points)
  881.         while total_points > current_points:
  882.             curve.points.new(0, 0)
  883.             current_points = len(curve.points)
  884.  
  885.     def set_values_for_curve(curve, curve_dict):
  886.         set_attr_if_exist(curve, "extend", curve_dict["extend"])
  887.         remove_all_curve_points(curve)
  888.         recreat_points(curve, len(curve_dict["points"]))
  889.         for index, point_dict in enumerate(curve_dict["points"]):
  890.             p = curve.points[index]
  891.             p.handle_type = point_dict["handle_type"]
  892.             p.location = tuple(point_dict["location"])
  893.  
  894.     curve_mapping.black_level = tuple(value_dict["black_level"])
  895.     curve_mapping.clip_max_x = value_dict["clip_max_x"]
  896.     curve_mapping.clip_max_y = value_dict["clip_max_y"]
  897.     curve_mapping.clip_min_x = value_dict["clip_min_x"]
  898.     curve_mapping.clip_min_y = value_dict["clip_min_y"]
  899.     curve_mapping.tone = value_dict["tone"]
  900.     curve_mapping.use_clip = value_dict["use_clip"]
  901.     curve_mapping.white_level = tuple(value_dict["white_level"])
  902.     curves_dict = value_dict["curves"]
  903.     for index, curve_dict in enumerate(curves_dict):
  904.         set_values_for_curve(curve_mapping.curves[index], curve_dict)
  905.     curve_mapping.update()
  906.  
  907. def set_attr_if_exist(obj, attr, value):
  908.     if hasattr(obj, attr) and value != "None":
  909.         setattr(obj, attr, value)
  910.  
  911. def remove_all_curve_points(curve):
  912.     while len(curve.points) > 2:
  913.         points = curve.points
  914.         for p in points:
  915.             try:
  916.                 curve.points.remove(p)
  917.             except:
  918.                 print("(Safe to ignore)Unable to remove curve point")
  919.  
  920.  
  921.  
  922. def set_values_for_ColorMapping(color_mapping, value_dict):
  923.     color_mapping.blend_color = tuple(value_dict["blend_color"])
  924.     color_mapping.blend_factor = value_dict["blend_factor"]
  925.     color_mapping.blend_type = value_dict["blend_type"]
  926.     color_mapping.brightness = value_dict["brightness"]
  927.     color_mapping.contrast = value_dict["contrast"]
  928.     color_mapping.saturation = value_dict["saturation"]
  929.     color_mapping.use_color_ramp = value_dict["use_color_ramp"]
  930.  
  931.  
  932. def set_values_for_ImageUser(image_user, value_dict):
  933.     image_user.frame_current = value_dict["frame_current"]
  934.     image_user.frame_duration = value_dict["frame_duration"]
  935.     image_user.frame_offset = value_dict["frame_offset"]
  936.     image_user.frame_start = value_dict["frame_start"]
  937.     image_user.use_cyclic = value_dict["use_cyclic"]
  938.     image_user.use_auto_refresh = value_dict["use_auto_refresh"]
  939.  
  940.  
  941.  
  942. def roman_noodles_shader_map(material, props_txt_path, pathtool):
  943.     #store new link function to variable
  944.     link = material.node_tree.links.new
  945.    
  946.     #store new node function to variable
  947.     new_node = material.node_tree.nodes.new
  948.    
  949.     #assign Principled BSDF to a variable so can be referenced later
  950.     #so that nodes can link to it
  951.     principled_node = material.node_tree.nodes.get('Principled BSDF')
  952.     #add subsurface skin settings to principled BSDF node
  953.     #if the material is for skin
  954.     if pathtool.is_material_skin == True:
  955.         principled_node.subsurface_method = "RANDOM_WALK"
  956.         principled_node.inputs[1].default_value = 0.03
  957.         principled_node.inputs["Subsurface Color"].default_value = (0.8, 0, 0, 1)
  958.        
  959.     principled_node.inputs["Specular"].default_value = 0.064
  960.  
  961.     #start adding all nodes and respective links to shader map
  962.     #--------------add everything except image texture nodes
  963.     srgb_node = new_node("ShaderNodeSeparateRGB")
  964.     srgb_node.location = (-150,150) # x,y
  965.     link(srgb_node.outputs[2], principled_node.inputs["Metallic"])
  966.  
  967.     ramp_node_1 = new_node("ShaderNodeValToRGB")
  968.     ramp_node_1.location = (-450,50) # x,y
  969.     ramp_node_1.color_ramp.elements[1].position = 0.209
  970.     link(ramp_node_1.outputs[0], principled_node.inputs["Roughness"])
  971.    
  972.     bump_node = new_node("ShaderNodeBump")
  973.     bump_node.location = (-200,-200) # x,y
  974.     bump_node.inputs[0].default_value = 0.175
  975.     link(bump_node.outputs[0], principled_node.inputs["Normal"])
  976.    
  977.     rgbbw_node = new_node("ShaderNodeRGBToBW")
  978.     rgbbw_node.location = (-150,0) # x,y
  979.     link(rgbbw_node.outputs[0], ramp_node_1.inputs["Fac"])
  980.    
  981.     normap_node = new_node(type="ShaderNodeNormalMap")
  982.     normap_node.location = (-400,-200) # x,y
  983.     link(normap_node.outputs[0], bump_node.inputs["Normal"])
  984.    
  985.     #only add skin related nodes for height map
  986.     #if material is for skin
  987.     if pathtool.is_material_skin == True:
  988.         map_node = new_node(type="ShaderNodeMapping")
  989.         map_node.location = (-300,-450) # x,y
  990.         map_node.inputs[3].default_value[0] = 12
  991.         map_node.inputs[3].default_value[1] = 12
  992.         map_node.inputs[3].default_value[2] = 12
  993.        
  994.         textcoord_node = new_node(type="ShaderNodeTexCoord")
  995.         textcoord_node.location = (-500,-450) # x,y
  996.         link(textcoord_node.outputs[2], map_node.inputs[0])
  997.  
  998.    
  999.     #open the propstxt file for the material and find the
  1000.     #texture locations from it
  1001.     #with just means open and close file
  1002.     with open(props_txt_path, 'r') as f:
  1003.         #read entire file to one string
  1004.         data = f.read()
  1005.         #find all matches through regex to the string Texture2D' with capture group
  1006.         #any character zero to unlimited times and ending with '
  1007.         #also store capture groups into a list variable
  1008.         match_list = re.findall("Texture2D\'(.*)\.", data)
  1009.    
  1010.     #only add image textures nodes if Include Image Textures
  1011.     #in panel is true
  1012.     if pathtool.is_add_img_textures:
  1013.         #---------------add image texture nodes    
  1014.         #example loading image
  1015.         #texImage.image = bpy.dat.images.load(
  1016.         #"C:\Users\myName\Downloads\Textures\Downloaded\flooring5.jpg")
  1017.        
  1018.         #add height map if Add Skin Related Nodes is checked and
  1019.         #add Height Map Skin Texture is checked
  1020.         #add the height map image texture and load the user defined
  1021.         #height map image
  1022.        
  1023.         if pathtool.is_add_img_textures == True and pathtool.is_material_skin == True and pathtool.is_add_height_map == True:
  1024.             height_map_path = pathtool.height_map_path
  1025.            
  1026.             height_map_node = new_node('ShaderNodeTexImage')
  1027.             height_map_node.location = (-100,-450) #x,y
  1028.             height_map_node.image = bpy.data.images.load(height_map_path)
  1029.             link(height_map_node.outputs[0], bump_node.inputs[2])
  1030.             link(map_node.outputs[0], height_map_node.inputs[0])
  1031.             height_map_node.interpolation = "Cubic"
  1032.        
  1033.        
  1034.         #use loop to go through all locations
  1035.         #specified in props.txt file
  1036.         #and create image texture nodes +
  1037.         #load all images for image textures
  1038.         for tex_location in match_list:
  1039.            
  1040.             #fetch last 6 characters in path which will tell you what
  1041.             #the current texture is in charge of e.g slice _BC off
  1042.             #/Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC
  1043.             #longest id is _ORM or _BDE 4 characters
  1044.             tex_id = tex_location[-6:]
  1045.            
  1046.             #if the folder path is a relative path
  1047.             #turn it into an absolute one
  1048.             #as relative paths cause problems
  1049.             #when trying to load an image
  1050.             abs_export_folder_path = bpy.path.abspath(pathtool.export_folder_path)
  1051.            
  1052.             # Returns user specified export game folder path
  1053.             # with first character removed
  1054.             # reason why is there would be double up of \/ when
  1055.             #concatenating strings
  1056.             user_tex_folder = abs_export_folder_path[:-1]
  1057.            
  1058.             #replace forward slash with backslash reason why is
  1059.             # when concatenating complete path looks like this
  1060.             #if no replace path looks like e.g. C:\Nyan\Dwight Recolor\Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC
  1061.             #which is weird
  1062.             #backslash is used to escape backslash character
  1063.             tex_location = tex_location.replace("/","\\")
  1064.            
  1065.             #if the user selects the game folder instead of the
  1066.             #parent folder, the first 5 characters of
  1067.             #the user input box: user_tex_folder will be "Game"
  1068.             #so we remove "Game\" from the tex_location
  1069.             #to avoid a double up
  1070.             #this is extra redundancy so if the
  1071.             #user chooses either the Game folder or
  1072.             #the parent folder of the Game folder
  1073.             #both options will work
  1074.             if user_tex_folder[-4:] == "Game":
  1075.                 #backslash is used to escape backslash character
  1076.                 tex_location = tex_location.replace("\\Game","")
  1077.      
  1078.             #must string concatenate the user specified texture location path to
  1079.             #the texture location
  1080.             #as the tex_location will only be
  1081.             #e.g /Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC
  1082.             #this does not provide a complete path to where the user exported
  1083.             #the texture
  1084.             #we need e.g. C:\Nyan\Dwight Recolor\Game\Characters
  1085.             #\Slashers\Bear\Textures\Outfit01\T_BEHead01_BC
  1086.             complete_path = user_tex_folder + tex_location + ".tga"
  1087.  
  1088.             #If the texture is listed in the
  1089.             #props.txt file and it is one of the
  1090.             #image textures we are interested in we will add the node
  1091.             #and load the corresponding image
  1092.                  
  1093.             #check what the last two/three characters are of the id
  1094.             #and look for the specific ids we are interested in
  1095.             #identifier
  1096.             if tex_id.endswith("_BC"):
  1097.                 bc_node = new_node('ShaderNodeTexImage')
  1098.                 bc_node.location = (-300,450) #x,y
  1099.                 bc_node.image = bpy.data.images.load(complete_path)
  1100.                 link(bc_node.outputs[0], principled_node.inputs[0])
  1101.                 link(bc_node.outputs[0], rgbbw_node.inputs[0])
  1102.                 bc_node.interpolation = "Cubic"
  1103.                
  1104.             elif tex_id.endswith("_ORM"):
  1105.                 orm_node = new_node('ShaderNodeTexImage')
  1106.                 orm_node.location = (-750, 300) #x,y
  1107.                 orm_node.image = bpy.data.images.load(complete_path)
  1108.                 link(orm_node.outputs[0], srgb_node.inputs[0])
  1109.                 orm_node.image.colorspace_settings.name = "Non-Color"
  1110.                
  1111.             elif tex_id.endswith("_N"):
  1112.                 n_node = new_node('ShaderNodeTexImage')
  1113.                 n_node.location = (-800,-200) #x,y
  1114.                 n_node.image = bpy.data.images.load(complete_path)
  1115.                 link(n_node.outputs[0], normap_node.inputs[1])
  1116.                 n_node.image.colorspace_settings.name = "Non-Color"
  1117.                
  1118.             elif tex_id.endswith("_M"):
  1119.                 #add ramp node to connect to M node and control
  1120.                 #alpha if alpha transparency is required
  1121.                 ramp_node_2 = new_node("ShaderNodeValToRGB")
  1122.                 ramp_node_2.location = (-750,20) # x,y
  1123.                 ramp_node_2.color_ramp.elements[1].position = 0.95
  1124.                 link(ramp_node_2.outputs[0], principled_node.inputs["Alpha"])
  1125.                
  1126.                 m_node = new_node('ShaderNodeTexImage')
  1127.                 m_node.location = (-1100,20) #x,y
  1128.                 m_node.image = bpy.data.images.load(complete_path)
  1129.                 link(m_node.outputs[0], ramp_node_2.inputs[0])
  1130.                 material.blend_method = 'CLIP'
  1131.  
  1132.  
  1133.  
  1134.  
  1135.  
  1136.  
  1137. classes = [PathProperties, LOADUESHADERSCRIPT_PT_main_panel,
  1138. LOADUESHADERSCRIPT_OT_add_basic, LOADUESHADERSCRIPT_OT_add_basic_all]
  1139.  
  1140. def register():
  1141.     for cls in classes:
  1142.         bpy.utils.register_class(cls)
  1143.        
  1144.         #register path_tool as a type which has all
  1145.         #the user input properties from the properties class
  1146.         bpy.types.Scene.path_tool = bpy.props.PointerProperty(type = PathProperties)
  1147.  
  1148. def unregister():
  1149.     for cls in classes:
  1150.         bpy.utils.unregister_class(cls)
  1151.        
  1152.         #unregister path_tool as a type
  1153.         del bpy.types.Scene.path_tool
  1154.  
  1155.  
  1156. if __name__ == "__main__":
  1157.     register()
  1158.  
Advertisement
Advertisement
Advertisement
RAW Paste Data Copied
Advertisement