# gpl: author Silvio Falcinelli. Fixes by angavrilov and others. # special thanks to user blenderartists.org cmomoney # -*- coding: utf-8 -*- import bpy from os import path as os_path from bpy.types import Operator from bpy.props import ( BoolProperty, EnumProperty, ) from .warning_messages_utils import ( warning_messages, c_is_cycles_addon_enabled, c_data_has_materials, collect_report, ) # ----------------------------------------------------------------------------- # Globals # # switch for operator's function called after AutoNodeInitiate CHECK_AUTONODE = False # set the node color for baked images (default greenish) NODE_COLOR = (0.32, 0.75, 0.32) # set the node color for the paint base images (default reddish) NODE_COLOR_PAINT = (0.6, 0.0, 0.0) # color for sculpt/texture painting setting (default clay the last entry is Roughness) PAINT_SC_COLOR = (0.80, 0.75, 0.54, 0.9) CLAY_GLOSSY = (0.38, 0.032, 0.023, 1) # ----------------------------------------------------------------------------- # Functions # def AutoNodeSwitch(renderer="CYCLES", switch="OFF", operator=None): mats = bpy.data.materials use_nodes = (True if switch in ("ON") else False) warn_message = ('BI_SW_NODES_ON' if switch in ("ON") else 'BI_SW_NODES_OFF') warn_message_2 = ('CYC_SW_NODES_ON' if switch in ("ON") else 'CYC_SW_NODES_OFF') for cmat in mats: cmat.use_nodes = use_nodes renders = ('CYCLES' if renderer and renderer == "CYCLES" else 'BLENDER_RENDER') bpy.context.scene.render.engine = renders if operator: warning_messages(operator, (warn_message_2 if renders in ('CYCLES') else warn_message)) def SetFakeUserTex(): images = bpy.data.images for image in images: has_user = getattr(image, "users", -1) image_name = getattr(image, "name", "NONAME") if has_user == 0: image.use_fake_user = True collect_report("INFO: Set fake user for unused image: " + image_name) def BakingText(tex, mode, tex_type=None): collect_report("INFO: start bake texture named: " + tex.name) saved_img_path = None bpy.ops.object.mode_set(mode='OBJECT') sc = bpy.context.scene tmat = '' img = '' Robj = bpy.context.active_object for n in bpy.data.materials: if n.name == 'TMP_BAKING': tmat = n if not tmat: tmat = bpy.data.materials.new('TMP_BAKING') tmat.name = "TMP_BAKING" bpy.ops.mesh.primitive_plane_add() tm = bpy.context.active_object tm.name = "TMP_BAKING" tm.data.name = "TMP_BAKING" bpy.ops.object.select_pattern(extend=False, pattern="TMP_BAKING", case_sensitive=False) sc.objects.active = tm bpy.context.scene.render.engine = 'BLENDER_RENDER' tm.data.materials.append(tmat) if len(tmat.texture_slots.items()) == 0: tmat.texture_slots.add() tmat.texture_slots[0].texture_coords = 'UV' tmat.texture_slots[0].use_map_alpha = True tmat.texture_slots[0].texture = tex.texture tmat.texture_slots[0].use_map_alpha = True tmat.texture_slots[0].use_map_color_diffuse = False tmat.use_transparency = True tmat.alpha = 0 tmat.use_nodes = False tmat.diffuse_color = 1, 1, 1 bpy.ops.object.mode_set(mode='EDIT') bpy.ops.uv.unwrap() # clean up temporary baking images if any for n in bpy.data.images: if n.name == 'TMP_BAKING': n.user_clear() bpy.data.images.remove(n) if mode == "ALPHA" and tex.texture.type == 'IMAGE': sizeX = tex.texture.image.size[0] sizeY = tex.texture.image.size[1] else: bake_size = (int(sc.mat_specials.img_bake_size) if sc.mat_specials.img_bake_size else 1024) sizeX = bake_size sizeY = bake_size bpy.ops.image.new(name="TMP_BAKING", width=sizeX, height=sizeY, color=(0.0, 0.0, 0.0, 1.0), alpha=True, float=False) sc.render.engine = 'BLENDER_RENDER' img = bpy.data.images["TMP_BAKING"] img = bpy.data.images.get("TMP_BAKING") img.file_format = ("JPEG" if not mode == "ALPHA" else "PNG") # switch temporarly to 'IMAGE EDITOR' # other approaches are not reliable check_area = False store_area = bpy.context.area.type collect_report("INFO: Temporarly switching context to Image Editor") try: bpy.context.area.type = 'IMAGE_EDITOR' bpy.context.area.spaces[0].image = bpy.data.images["TMP_BAKING"] check_area = True except: collect_report("ERROR: Setting to Image Editor failed, Baking aborted") check_area = False if check_area: paths = bpy.path.abspath(sc.mat_specials.conv_path) tex_name = getattr(getattr(tex.texture, "image", None), "name", None) texture_name = (tex_name.rpartition(".")[0] if tex_name else tex.texture.name) new_tex_name = "baked" name_append = ("_BAKING" if mode == "ALPHA" and tex.texture.type == 'IMAGE' else "_PTEXT") new_appendix = (".jpg" if not mode == "ALPHA" else ".png") if name_append in texture_name: new_tex_name = texture_name elif tex_type: new_tex_name = tex_type + name_append else: new_tex_name = texture_name + name_append img.filepath_raw = paths + new_tex_name + new_appendix saved_img_path = img.filepath_raw sc.render.bake_type = 'ALPHA' sc.render.use_bake_selected_to_active = True sc.render.use_bake_clear = True # try to bake if it fails give report try: bpy.ops.object.bake_image() img.save() except: # no return value, so the image loading is skipped saved_img_path = None collect_report("ERROR: Baking could not be completed. " "Check System Console for info") if store_area: bpy.context.area.type = store_area bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.delete() bpy.ops.object.select_pattern(extend=False, pattern=Robj.name, case_sensitive=False) sc.objects.active = Robj img.user_clear() bpy.data.images.remove(img) if tmat.users == 0: bpy.data.materials.remove(tmat) if saved_img_path: collect_report("------- Baking finished -------") return saved_img_path def AutoNodeInitiate(active=False, operator=None): # Checks with bpy.ops.material.check_converter_path # if it's possible to write in the output path # if it passes procedes with calling AutoNode # if CheckImagePath(operator): check_path = bpy.ops.material.check_converter_path() global CHECK_AUTONODE if 'FINISHED' in check_path: sc = bpy.context.scene CHECK_AUTONODE = True collect_report("_______________________", True, False) AutoNode(active, operator) if sc.mat_specials.SET_FAKE_USER: SetFakeUserTex() else: warning_messages(operator, 'DIR_PATH_CONVERT') def AutoNode(active=False, operator=None): global CHECK_AUTONODE sc = bpy.context.scene if active: # fix for empty slots by angavrilov mats = [slot.material for slot in bpy.context.active_object.material_slots if slot.material] else: mats = bpy.data.materials # No Materials for the chosen action - abort if not mats: CHECK_AUTONODE = False if operator: if active: act_obj = bpy.context.active_object warning_messages(operator, 'CONV_NO_OBJ_MAT', act_obj.name) else: warning_messages(operator, 'CONV_NO_SC_MAT') return for cmat in mats: # check for empty material (it will fall through the first check) test_empty = getattr(cmat, "name", None) if test_empty is None: collect_report("An empty material was hit, skipping") continue cmat.use_nodes = True TreeNodes = cmat.node_tree links = TreeNodes.links # Don't alter nodes of locked materials locked = False for n in TreeNodes.nodes: if n.type == 'ShaderNodeOutputMaterial': if n.label == 'Locked': locked = True break if not locked: # Convert this material from non-nodes to Cycles nodes shader = '' shtsl = '' Add_Emission = '' Add_Translucent = '' Mix_Alpha = '' sT = False # check if some link creation failed link_fail = False for n in TreeNodes.nodes: TreeNodes.nodes.remove(n) # Starting point is diffuse BSDF and output material # and a Color Ramp node shader = TreeNodes.nodes.new('ShaderNodeBsdfDiffuse') shader.location = 0, 470 shader_val = TreeNodes.nodes.new('ShaderNodeValToRGB') shader_val.location = 0, 270 shout = TreeNodes.nodes.new('ShaderNodeOutputMaterial') shout.location = 200, 400 try: links.new(shader.outputs[0], shout.inputs[0]) links.new(shader.inputs[0], shader_val.outputs[0]) except: link_fail = True # Create other shader types only sculpt/texture paint mode is False sculpt_paint = sc.mat_specials.SCULPT_PAINT if sculpt_paint is False: cmat_is_transp = cmat.use_transparency and cmat.alpha < 1 if not cmat.raytrace_mirror.use and not cmat_is_transp: if not shader.type == 'ShaderNodeBsdfDiffuse': collect_report("INFO: Make DIFFUSE shader node for: " + cmat.name) TreeNodes.nodes.remove(shader) shader = TreeNodes.nodes.new('ShaderNodeBsdfDiffuse') shader.location = 0, 470 try: links.new(shader.outputs[0], shout.inputs[0]) links.new(shader.inputs[0], shader_val.outputs[0]) except: link_fail = True if cmat.raytrace_mirror.use and cmat.raytrace_mirror.reflect_factor > 0.001 and cmat_is_transp: if not shader.type == 'ShaderNodeBsdfGlass': collect_report("INFO: Make GLASS shader node for: " + cmat.name) TreeNodes.nodes.remove(shader) shader = TreeNodes.nodes.new('ShaderNodeBsdfGlass') shader.location = 0, 470 try: links.new(shader.outputs[0], shout.inputs[0]) links.new(shader.inputs[0], shader_val.outputs[0]) except: link_fail = True if cmat.raytrace_mirror.use and not cmat_is_transp and cmat.raytrace_mirror.reflect_factor > 0.001: if not shader.type == 'ShaderNodeBsdfGlossy': collect_report("INFO: Make MIRROR shader node for: " + cmat.name) TreeNodes.nodes.remove(shader) shader = TreeNodes.nodes.new('ShaderNodeBsdfGlossy') shader.location = 0, 520 try: links.new(shader.outputs[0], shout.inputs[0]) links.new(shader.inputs[0], shader_val.outputs[0]) except: link_fail = True if cmat.emit > 0.001: if (not shader.type == 'ShaderNodeEmission' and not cmat.raytrace_mirror.reflect_factor > 0.001 and not cmat_is_transp): collect_report("INFO: Mix EMISSION shader node for: " + cmat.name) TreeNodes.nodes.remove(shader) shader = TreeNodes.nodes.new('ShaderNodeEmission') shader.location = 0, 450 try: links.new(shader.outputs[0], shout.inputs[0]) links.new(shader.inputs[0], shader_val.outputs[0]) except: link_fail = True else: if not Add_Emission: collect_report("INFO: Add EMISSION shader node for: " + cmat.name) shout.location = 550, 330 Add_Emission = TreeNodes.nodes.new('ShaderNodeAddShader') Add_Emission.location = 370, 490 shem = TreeNodes.nodes.new('ShaderNodeEmission') shem.location = 180, 380 try: links.new(Add_Emission.outputs[0], shout.inputs[0]) links.new(shem.outputs[0], Add_Emission.inputs[1]) links.new(shader.outputs[0], Add_Emission.inputs[0]) except: link_fail = True shem.inputs['Color'].default_value = (cmat.diffuse_color.r, cmat.diffuse_color.g, cmat.diffuse_color.b, 1) shem.inputs['Strength'].default_value = cmat.emit if cmat.translucency > 0.001: collect_report("INFO: Add BSDF_TRANSLUCENT shader node for: " + cmat.name) shout.location = 770, 330 Add_Translucent = TreeNodes.nodes.new('ShaderNodeAddShader') Add_Translucent.location = 580, 490 shtsl = TreeNodes.nodes.new('ShaderNodeBsdfTranslucent') shtsl.location = 400, 350 try: links.new(Add_Translucent.outputs[0], shout.inputs[0]) links.new(shtsl.outputs[0], Add_Translucent.inputs[1]) if Add_Emission: links.new(Add_Emission.outputs[0], Add_Translucent.inputs[0]) else: links.new(shader.outputs[0], Add_Translucent.inputs[0]) except: link_fail = True shtsl.inputs['Color'].default_value = (cmat.translucency, cmat.translucency, cmat.translucency, 1) if sculpt_paint is False: shader.inputs['Color'].default_value = (cmat.diffuse_color.r, cmat.diffuse_color.g, cmat.diffuse_color.b, 1) else: # Create Clay Material (Diffuse, Glossy, Layer Weight) shader.inputs['Color'].default_value = PAINT_SC_COLOR shader.inputs['Roughness'].default_value = 0.9 # remove Color Ramp and links from the default shader and reroute try: shout.location = 400, 460 for link in links: links.remove(link) TreeNodes.nodes.remove(shader_val) clay_frame = TreeNodes.nodes.new('NodeFrame') clay_frame.name = 'Clay Material' clay_frame.label = 'Clay Material' sh_glossy = TreeNodes.nodes.new('ShaderNodeBsdfGlossy') sh_glossy.location = 0, 350 sh_glossy.inputs['Color'].default_value = CLAY_GLOSSY sh_mix = TreeNodes.nodes.new('ShaderNodeMixShader') sh_mix.location = 200, 460 sh_weight = TreeNodes.nodes.new('ShaderNodeLayerWeight') sh_weight.location = 0, 590 links.new(sh_mix.outputs[0], shout.inputs[0]) links.new(sh_weight.outputs[1], sh_mix.inputs[0]) links.new(shader.outputs[0], sh_mix.inputs[1]) links.new(sh_glossy.outputs[0], sh_mix.inputs[2]) # set frame as parent to everything for clay_node in (shader, sh_glossy, sh_mix, sh_weight): clay_node.parent = clay_frame except: collect_report("ERROR: Failure to create Clay Material") if not sculpt_paint: if shader.type == 'ShaderNodeBsdfDiffuse': shader.inputs['Roughness'].default_value = cmat.specular_intensity if shader.type == 'ShaderNodeBsdfGlossy': shader.inputs['Roughness'].default_value = 1 - cmat.raytrace_mirror.gloss_factor if shader.type == 'ShaderNodeBsdfGlass': shader.inputs['Roughness'].default_value = 1 - cmat.raytrace_mirror.gloss_factor shader.inputs['IOR'].default_value = cmat.raytrace_transparency.ior if shader.type == 'ShaderNodeEmission': shader.inputs['Strength'].default_value = cmat.emit # texture presence check is_textures = False for tex in cmat.texture_slots: if tex: if not is_textures: is_textures = True break if is_textures: # collect the texture nodes created # for spreading a bit the texture nodes tex_node_collect = [] sM = True for tex in cmat.texture_slots: sT = False tex_use = getattr(tex, "use", None) baked_path = None if tex_use: tex_node_loc = -200, 450 ma_alpha = getattr(tex, "use_map_alpha", None) sM = (False if ma_alpha else True) img = None if tex.texture.type == 'IMAGE': if sc.mat_specials.EXTRACT_ALPHA and tex.texture.use_alpha: if (not os_path.exists(bpy.path.abspath(tex.texture.image.filepath + "_BAKING.png")) or sc.mat_specials.EXTRACT_OW): baked_path = BakingText(tex, 'ALPHA') if baked_path: try: img = bpy.data.images.load(baked_path) collect_report("INFO: Loading Baked texture path:") collect_report(baked_path) except: collect_report("ERROR: Baked image could not be loaded") else: has_image = getattr(tex.texture, "image", None) if has_image: img = has_image if img: img_name = (img.name if hasattr(img, "name") else "NO NAME") shtext = TreeNodes.nodes.new('ShaderNodeTexImage') shtext.location = tex_node_loc shtext.image = img shtext.name = img_name shtext.label = "Image " + img_name if baked_path: shtext.use_custom_color = True shtext.color = NODE_COLOR collect_report("INFO: Creating Image Node for image: " + img_name) tex_node_collect.append(shtext) sT = True else: collect_report("ERROR: A problem occured with loading an image for {} " "(possibly missing)".format(tex.texture.name)) else: if sc.mat_specials.EXTRACT_PTEX or (sc.mat_specials.EXTRACT_ALPHA and ma_alpha): if (not os_path.exists(bpy.path.abspath(tex.texture.name + "_PTEXT.jpg")) or sc.mat_specials.EXTRACT_OW): tex_type = tex.texture.type.lower() collect_report("Attempting to Extract Procedural Texture type: " + tex_type) baked_path = BakingText(tex, 'PTEX', tex_type) if baked_path: try: img = bpy.data.images.load(baked_path) collect_report("Loading Baked texture path:") collect_report(baked_path) img_name = (img.name if hasattr(img, "name") else "NO NAME") shtext = TreeNodes.nodes.new('ShaderNodeTexImage') shtext.location = tex_node_loc shtext.image = img shtext.name = img_name shtext.label = "Baked Image " + img_name shtext.use_custom_color = True shtext.color = NODE_COLOR collect_report("Creating Image Node for baked image: " + img_name) tex_node_collect.append(shtext) sT = True except: collect_report("ERROR: Failure to load baked image: " + img_name) else: collect_report("ERROR: Failure during baking, no images loaded") if sculpt_paint is False: if (cmat_is_transp and cmat.raytrace_transparency.ior == 1 and not cmat.raytrace_mirror.use and sM): if not shader.type == 'ShaderNodeBsdfTransparent': collect_report("INFO: Make TRANSPARENT shader node for: " + cmat.name) TreeNodes.nodes.remove(shader) shader = TreeNodes.nodes.new('ShaderNodeBsdfTransparent') shader.location = 0, 470 try: links.new(shader.outputs[0], shout.inputs[0]) except: link_fail = True shader.inputs['Color'].default_value = (cmat.diffuse_color.r, cmat.diffuse_color.g, cmat.diffuse_color.b, 1) if sT and sculpt_paint is False: if tex.use_map_color_diffuse: try: links.new(shtext.outputs[0], shader_val.inputs[0]) except: pass if tex.use_map_emit: if not Add_Emission: collect_report("INFO: Mix EMISSION + Texture shader node for: " + cmat.name) intensity = 0.5 + (tex.emit_factor / 2) shout.location = 550, 330 Add_Emission = TreeNodes.nodes.new('ShaderNodeAddShader') Add_Emission.name = "Add_Emission" Add_Emission.location = 370, 490 shem = TreeNodes.nodes.new('ShaderNodeEmission') shem.location = 180, 380 try: links.new(Add_Emission.outputs[0], shout.inputs[0]) links.new(shem.outputs[0], Add_Emission.inputs[1]) links.new(shader.outputs[0], Add_Emission.inputs[0]) links.new(shtext.outputs[0], shem.inputs[0]) except: link_fail = True shem.inputs['Color'].default_value = (cmat.diffuse_color.r, cmat.diffuse_color.g, cmat.diffuse_color.b, 1) shem.inputs['Strength'].default_value = intensity * 2 if tex.use_map_mirror: try: links.new(shtext.outputs[0], shader_val.inputs[0]) except: link_fail = True if tex.use_map_translucency: if not Add_Translucent: collect_report("INFO: Add Translucency + Texture shader node for: " + cmat.name) intensity = 0.5 + (tex.emit_factor / 2) shout.location = 550, 330 Add_Translucent = TreeNodes.nodes.new('ShaderNodeAddShader') Add_Translucent.name = "Add_Translucent" Add_Translucent.location = 370, 290 shtsl = TreeNodes.nodes.new('ShaderNodeBsdfTranslucent') shtsl.location = 180, 240 try: links.new(shtsl.outputs[0], Add_Translucent.inputs[1]) if Add_Emission: links.new(Add_Translucent.outputs[0], shout.inputs[0]) links.new(Add_Emission.outputs[0], Add_Translucent.inputs[0]) pass else: links.new(Add_Translucent.outputs[0], shout.inputs[0]) links.new(shader.outputs[0], Add_Translucent.inputs[0]) links.new(shtext.outputs[0], shtsl.inputs[0]) except: link_fail = True if tex.use_map_alpha: if not Mix_Alpha: collect_report("INFO: Mix Alpha + Texture shader node for: " + cmat.name) shout.location = 750, 330 Mix_Alpha = TreeNodes.nodes.new('ShaderNodeMixShader') Mix_Alpha.name = "Add_Alpha" Mix_Alpha.location = 570, 290 sMask = TreeNodes.nodes.new('ShaderNodeBsdfTransparent') sMask.location = 250, 180 tMask, imask = None, None # search if the texture node already exists, if not create nodes = getattr(cmat.node_tree, "nodes", None) img_name = getattr(img, "name", "NO NAME") for node in nodes: if type(node) == bpy.types.ShaderNodeTexImage: node_name = getattr(node, "name") if img_name in node_name: tMask = node collect_report("INFO: Using existing Texture Node for Mask: " + node_name) break if tMask is None: tMask = TreeNodes.nodes.new('ShaderNodeTexImage') tMask.location = tex_node_loc tex_node_collect.append(tMask) try: file_path = getattr(img, "filepath", None) if file_path: imask = bpy.data.images.load(file_path) else: imask = bpy.data.images.get(img_name) collect_report("INFO: Attempting to load image for Mask: " + img_name) except: collect_report("ERROR: Failure to load image for Mask: " + img_name) if imask: tMask.image = imask if tMask: try: links.new(Mix_Alpha.inputs[0], tMask.outputs[1]) links.new(shout.inputs[0], Mix_Alpha.outputs[0]) links.new(sMask.outputs[0], Mix_Alpha.inputs[1]) if not Add_Translucent: if Add_Emission: links.new(Mix_Alpha.inputs[2], Add_Emission.outputs[0]) else: links.new(Mix_Alpha.inputs[2], shader.outputs[0]) else: links.new(Mix_Alpha.inputs[2], Add_Translucent.outputs[0]) except: link_fail = True else: collect_report("ERROR: Mix Alpha could not be created " "(mask image could not be loaded)") if tex.use_map_normal: t = TreeNodes.nodes.new('ShaderNodeRGBToBW') t.location = -0, 300 try: links.new(t.outputs[0], shout.inputs[2]) links.new(shtext.outputs[1], t.inputs[0]) except: link_fail = True if sculpt_paint: try: # create a new image for texture painting and make it active img_size = (int(sc.mat_specials.img_bake_size) if sc.mat_specials.img_bake_size else 1024) paint_mat_name = getattr(cmat, "name", "NO NAME") paint_img_name = "Paint Base Image {}".format(paint_mat_name) bpy.ops.image.new(name=paint_img_name, width=img_size, height=img_size, color=(1.0, 1.0, 1.0, 1.0), alpha=True, float=False) img = bpy.data.images.get(paint_img_name) img_name = (img.name if hasattr(img, "name") else "NO NAME") shtext = TreeNodes.nodes.new('ShaderNodeTexImage') shtext.location = tex_node_loc shtext.image = img shtext.name = img_name shtext.label = "Paint: " + img_name shtext.use_custom_color = True shtext.color = NODE_COLOR_PAINT shtext.select = True collect_report("INFO: Creating Image Node for Painting: " + img_name) collect_report("WARNING: Don't forget to save it on Disk") tex_node_collect.append(shtext) except: collect_report("ERROR: Failed to create image and node for Texture Painting") # spread the texture nodes, create a node frame if necessary # create texture coordinate and mapping too row_node, col_node = -1, False check_frame = bool(len(tex_node_collect) > 1) node_frame, tex_map, node_f_coord = None, None, None tex_map_collection, tex_map_coord = [], None if check_frame: node_frame = TreeNodes.nodes.new('NodeFrame') node_frame.name = 'Converter Textures' node_frame.label = 'Converter Textures' node_f_coord = TreeNodes.nodes.new('NodeFrame') node_f_coord.name = "Coordinates" node_f_coord.label = "Coordinates" if tex_node_collect: tex_map_coord = TreeNodes.nodes.new('ShaderNodeTexCoord') tex_map_coord.location = -900, 575 for node_tex in tex_node_collect: row_node = (row_node + 1 if not col_node else row_node) col_node = not col_node tex_node_loc = (-(200 + (row_node * 150)), (400 if col_node else 650)) try: node_tex.location = tex_node_loc if check_frame: node_tex.parent = node_frame tex_node_name = getattr(node_tex, "name", "NO NAME") tex_map_name = "Mapping: {}".format(tex_node_name) tex_map = TreeNodes.nodes.new('ShaderNodeMapping') tex_map.width = 240 tex_map.hide = True tex_map.width_hidden = 150 tex_map.name = tex_map_name tex_map.label = tex_map_name tex_map_collection.append(tex_map) links.new(tex_map.outputs[0], node_tex.inputs[0]) except: link_fail = True continue if tex_map_collection: row_map_tex = -(350 + (row_node + 1) * 150) col_map_start = (((len(tex_map_collection) / 2) * 50) + 575) if tex_map_coord: tex_map_coord.location = ((row_map_tex - 250), 500) for maps in tex_map_collection: row_node += 1 tex_map_loc = row_map_tex, (-(row_node * 50) + col_map_start) try: maps.location = tex_map_loc if node_f_coord: maps.parent = node_f_coord links.new(maps.inputs[0], tex_map_coord.outputs['UV']) except: link_fail = True continue tex_node_collect, tex_map_collection = [], [] if link_fail: collect_report("ERROR: Some of the node links failed to connect") else: collect_report("No textures in the Scene, no Image Nodes to add") bpy.context.scene.render.engine = 'CYCLES' # ----------------------------------------------------------------------------- # Operator Classes # class mllock(Operator): bl_idname = "ml.lock" bl_label = "Lock" bl_description = "Lock/unlock this material against modification by conversions" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return (c_is_cycles_addon_enabled() and c_data_has_materials()) def execute(self, context): cmat = bpy.context.selected_objects[0].active_material TreeNodes = cmat.node_tree for n in TreeNodes.nodes: if n.type == 'ShaderNodeOutputMaterial': if n.label == 'Locked': n.label = '' else: n.label = 'Locked' return {'FINISHED'} class mlrefresh(Operator): bl_idname = "ml.refresh" bl_label = "Convert All Materials" bl_description = "Convert All Materials in the scene from non-nodes to Cycles" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return (c_is_cycles_addon_enabled() and c_data_has_materials()) def execute(self, context): AutoNodeInitiate(False, self) if CHECK_AUTONODE is True: enable_unwrap = bpy.context.scene.mat_specials.UV_UNWRAP if enable_unwrap: obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT") try: # it's possible to the active object would fail UV Unwrap bpy.ops.object.editmode_toggle() bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) bpy.ops.object.editmode_toggle() collect_report("INFO: UV Unwrapping active object " "{}".format(obj_name)) except: collect_report("ERROR: UV Unwrapping failed for " "active object {}".format(obj_name)) collect_report("Conversion finished !", False, True) return {'FINISHED'} class mlrefresh_active(Operator): bl_idname = "ml.refresh_active" bl_label = "Convert All Materials From Active Object" bl_description = "Convert all Active Object's Materials from non-nodes to Cycles" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return (c_is_cycles_addon_enabled() and c_data_has_materials() and context.active_object is not None) def execute(self, context): AutoNodeInitiate(True, self) if CHECK_AUTONODE is True: obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT") enable_unwrap = bpy.context.scene.mat_specials.UV_UNWRAP if enable_unwrap: try: # you can already guess it, what could happen here bpy.ops.object.editmode_toggle() bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) bpy.ops.object.editmode_toggle() collect_report("INFO: UV Unwrapping object {}".format(obj_name)) except: collect_report("ERROR: UV Unwrapping failed for " "object {}".format(obj_name)) collect_report("Conversion finished !", False, True) return {'FINISHED'} class mlrestore(Operator): bl_idname = "ml.restore" bl_label = "Switch Between Renderers" bl_description = ("Switch between Renderers \n" "(Doesn't create new nor converts existing materials)") bl_options = {'REGISTER', 'UNDO'} switcher = BoolProperty( name="Use Nodes", description="When restoring, switch Use Nodes On/Off", default=True ) renderer = EnumProperty( name="Renderer", description="Choose Cycles or Blender Internal", items=(('CYCLES', "Cycles", "Switch to Cycles"), ('BI', "Blender Internal", "Switch to Blender Internal")), default='CYCLES', ) @classmethod def poll(cls, context): return c_is_cycles_addon_enabled() def execute(self, context): if self.switcher: AutoNodeSwitch(self.renderer, "ON", self) else: AutoNodeSwitch(self.renderer, "OFF", self) return {'FINISHED'} def register(): bpy.utils.register_module(__name__) pass def unregister(): bpy.utils.unregister_module(__name__) pass if __name__ == "__main__": register()