diff options
-rw-r--r-- | materials_utils/__init__.py | 30 | ||||
-rw-r--r-- | materials_utils/materials_cycles_converter.py | 676 |
2 files changed, 472 insertions, 234 deletions
diff --git a/materials_utils/__init__.py b/materials_utils/__init__.py index fed59490..fb84c4a9 100644 --- a/materials_utils/__init__.py +++ b/materials_utils/__init__.py @@ -1699,6 +1699,10 @@ class MATERIAL_MT_scenemassive_opt(Menu): use_separator(self, context) layout.prop(sc.mat_specials, "SET_FAKE_USER", text="Set Fake User on unused images") use_separator(self, context) + layout.prop(sc.mat_specials, "SCULPT_PAINT", text="Sculpt/Texture paint mode") + use_separator(self, context) + layout.prop(sc.mat_specials, "UV_UNWRAP", text="Set Auto UV Unwrap (Active Object)") + use_separator(self, context) layout.label("Set the Bake Resolution") res = str(sc.mat_specials.img_bake_size) @@ -1798,14 +1802,20 @@ class MATERIAL_MT_biconv_help(Menu): def draw(self, context): layout = self.layout + layout.label(text="If possible, avoid multiple conversions in a row") layout.label(text="Save Your Work Often", icon="ERROR") use_separator(self, context) + layout.label(text="Try to link them manually using Mix Color nodes") + layout.label(text="Only the last Image in the stack gets linked to Shader") + layout.label(text="Current limitation:", icon="MOD_EXPLODE") + use_separator(self, context) layout.label(text="Select the texture loaded in the image node") layout.label(text="Press Ctrl/T to create the image nodes") layout.label(text="In the Node Editor, Select the Diffuse Node") layout.label(text="Enable Node Wrangler addon", icon="NODETREE") layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE") use_separator(self, context) + layout.label(text="Extract Alpha: the images have to have alpha channel") layout.label(text="The default path is the folder where the current .blend is") layout.label(text="During Baking, the script will check writting privileges") layout.label(text="Set the save path for extracting images with full access") @@ -1828,6 +1838,7 @@ class MATERIAL_MT_nodeconv_help(Menu): def draw(self, context): layout = self.layout + layout.label(text="If possible, avoid multiple conversions in a row") layout.label(text="Save Your Work Often", icon="ERROR") use_separator(self, context) layout.label(text="Relinking and removing some not needed nodes") @@ -1839,6 +1850,7 @@ class MATERIAL_MT_nodeconv_help(Menu): layout.label(text="Enable Node Wrangler addon", icon="NODETREE") layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE") use_separator(self, context) + layout.label(text="For Specular Nodes, Image color influence has to be enabled") layout.label(text="Generated images (i.e. Noise and others) are not converted") layout.label(text="The Converter report can point out to some failures") layout.label(text="Not all Files will produce good results", icon="ERROR") @@ -1894,20 +1906,36 @@ class material_specials_scene_props(PropertyGroup): EXTRACT_ALPHA = BoolProperty( attr="EXTRACT_ALPHA", default=False, + description=("Extract Alpha channel from non-procedural images \n" + "Don't use this option if the image doesn't have Alpha"), ) SET_FAKE_USER = BoolProperty( attr="SET_FAKE_USER", default=False, + description="Set fake user on unused images, so they can be kept in the .blend", ) EXTRACT_PTEX = BoolProperty( attr="EXTRACT_PTEX", default=False, + description="Extract procedural images and bake them to jpeg", ) EXTRACT_OW = BoolProperty( attr="Overwrite", default=False, description="Extract textures again instead of re-using priorly extracted textures", ) + SCULPT_PAINT = BoolProperty( + attr="SCULPT_PAINT", + default=False, + description=("Conversion geared towards sculpting and painting.\n" + "Creates a diffuse, glossy mixed with layer weight. \n" + "Image nodes are not connected"), + ) + UV_UNWRAP = BoolProperty( + attr="UV_UNWRAP", + default=False, + description=("Use automatical Angle based UV Unwrap of the active Object"), + ) img_bake_size = EnumProperty( name="Bake Image Size", description="Set the resolution size of baked images \n", @@ -1970,7 +1998,7 @@ class VIEW3D_MT_material_utils_pref(AddonPreferences): show_converters = BoolProperty( name="Enable Converters", default=True, - description=" \n ", + description="Enable Material Converters", ) set_preview_size = EnumProperty( diff --git a/materials_utils/materials_cycles_converter.py b/materials_utils/materials_cycles_converter.py index 838c5558..1b46c0f7 100644 --- a/materials_utils/materials_cycles_converter.py +++ b/materials_utils/materials_cycles_converter.py @@ -3,7 +3,6 @@ # -*- coding: utf-8 -*- import bpy -import os from os import path as os_path from bpy.types import Operator from bpy.props import ( @@ -23,8 +22,14 @@ from .warning_messages_utils import ( # switch for operator's function called after AutoNodeInitiate CHECK_AUTONODE = False -# set the node color for baked textures (default greenish) +# 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 # @@ -61,6 +66,7 @@ def SetFakeUserTex(): 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 = '' @@ -113,43 +119,59 @@ def BakingText(tex, mode, tex_type=None): bpy.ops.image.new(name="TMP_BAKING", width=sizeX, height=sizeY, color=(0.0, 0.0, 0.0, 1.0), alpha=True, float=False) - bpy.data.screens['UV Editing'].areas[1].spaces[0].image = bpy.data.images["TMP_BAKING"] + 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") - 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 + # 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.ops.object.bake_image() - img.save() + bpy.context.area.type = 'IMAGE_EDITOR' + bpy.context.area.spaces[0].image = bpy.data.images["TMP_BAKING"] + check_area = True 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") + 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() @@ -162,7 +184,7 @@ def BakingText(tex, mode, tex_type=None): bpy.data.materials.remove(tmat) if saved_img_path: - collect_report("________________________________________") + collect_report("------- Baking finished -------") return saved_img_path @@ -179,11 +201,10 @@ def AutoNodeInitiate(active=False, operator=None): if 'FINISHED' in check_path: sc = bpy.context.scene CHECK_AUTONODE = True - collect_report("________________________________________", True, False) + collect_report("_______________________", True, False) AutoNode(active, operator) if sc.mat_specials.SET_FAKE_USER: SetFakeUserTex() - collect_report("Conversion finished !", False, True) else: warning_messages(operator, 'DIR_PATH_CONVERT') @@ -236,144 +257,201 @@ def AutoNode(active=False, operator=None): 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 - shader = TreeNodes.nodes.new('ShaderNodeValToRGB') - shader.location = 0, 270 + # 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 - links.new(shader.outputs[0], shout.inputs[0]) - - 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 - links.new(shader.outputs[0], shout.inputs[0]) - - 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 - links.new(shader.outputs[0], shout.inputs[0]) - - 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 - links.new(shader.outputs[0], shout.inputs[0]) - - 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 - links.new(shader.outputs[0], shout.inputs[0]) - 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 - - 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]) - - 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 - - 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]) - pass - else: - links.new(shader.outputs[0], Add_Translucent.inputs[0]) - pass - shtsl.inputs['Color'].default_value = (cmat.translucency, - cmat.translucency, - cmat.translucency, 1) - - shader.inputs['Color'].default_value = (cmat.diffuse_color.r, - cmat.diffuse_color.g, - cmat.diffuse_color.b, 1) - - 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 frame check and tex count (if bigger than 1 add frame) - frame_check, tex_count, node_frame = False, 0, None + 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: - tex_count += 1 - if not frame_check: - frame_check = True - if tex_count > 1: + if not is_textures: + is_textures = True break - if frame_check: - if tex_count > 1: - node_frame = TreeNodes.nodes.new('NodeFrame') - node_frame.name = 'Converter Textures' - node_frame.label = 'Converter Textures' - - # count the number of texture nodes created + if is_textures: + # collect the texture nodes created # for spreading a bit the texture nodes - row_node, col_node = -1, False + tex_node_collect = [] + sM = True - baked_path = None for tex in cmat.texture_slots: sT = False tex_use = getattr(tex, "use", None) + baked_path = None if tex_use: - 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) + 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: @@ -381,30 +459,35 @@ def AutoNode(active=False, operator=None): os_path.exists(bpy.path.abspath(tex.texture.image.filepath + "_BAKING.png")) or sc.mat_specials.EXTRACT_OW): baked_path = BakingText(tex, 'ALPHA') - try: - if baked_path: - img = bpy.data.images.load(baked_path) - collect_report("INFO: Loading Baked texture path:") - collect_report(baked_path) - else: - img = tex.texture.image - - 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) - if node_frame: - shtext.parent = node_frame - sT = True - except: - collect_report("ERROR: A problem occured with loading an image for {} " - "(possibly missing)".format(tex.texture.name)) + 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 @@ -427,37 +510,37 @@ def AutoNode(active=False, operator=None): shtext.use_custom_color = True shtext.color = NODE_COLOR collect_report("Creating Image Node for baked image: " + img_name) - if node_frame: - shtext.parent = node_frame + 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 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 - links.new(shader.outputs[0], shout.inputs[0]) + 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) + shader.inputs['Color'].default_value = (cmat.diffuse_color.r, + cmat.diffuse_color.g, + cmat.diffuse_color.b, 1) - if sT: + if sT and sculpt_paint is False: if tex.use_map_color_diffuse: - coordout = 2 - map = TreeNodes.nodes.new('ShaderNodeMapping') - map.location = -500, 570 - map.width = 240 - coord = TreeNodes.nodes.new('ShaderNodeTexCoord') - coord.location = -800, 570 - links.new(shtext.outputs[0], shader.inputs[0]) - links.new(map.outputs[0], shtext.inputs[0]) - links.new(coord.outputs[coordout], map.inputs[0]) + 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) @@ -471,19 +554,24 @@ def AutoNode(active=False, operator=None): shem = TreeNodes.nodes.new('ShaderNodeEmission') shem.location = 180, 380 - 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]) + 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 - links.new(shtext.outputs[0], shem.inputs[0]) - if tex.use_map_mirror: - links.new(shader.inputs[0], shtext.outputs[0]) + try: + links.new(shtext.outputs[0], shader_val.inputs[0]) + except: + link_fail = True if tex.use_map_translucency: if not Add_Translucent: @@ -497,18 +585,20 @@ def AutoNode(active=False, operator=None): shtsl = TreeNodes.nodes.new('ShaderNodeBsdfTranslucent') shtsl.location = 180, 240 + try: + links.new(shtsl.outputs[0], Add_Translucent.inputs[1]) - 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]) + 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]) + links.new(shtext.outputs[0], shtsl.inputs[0]) + except: + link_fail = True if tex.use_map_alpha: if not Mix_Alpha: @@ -535,11 +625,8 @@ def AutoNode(active=False, operator=None): if tMask is None: tMask = TreeNodes.nodes.new('ShaderNodeTexImage') - - if node_frame: - tMask.parent = node_frame - tex_node_loc = -(200 + ((row_node + 1) * 150)), (650 if col_node else 400) tMask.location = tex_node_loc + tex_node_collect.append(tMask) try: file_path = getattr(img, "filepath", None) @@ -555,17 +642,20 @@ def AutoNode(active=False, operator=None): tMask.image = imask if tMask: - 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]) + 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], shader.outputs[0]) - else: - links.new(Mix_Alpha.inputs[2], Add_Translucent.outputs[0]) + 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)") @@ -573,8 +663,106 @@ def AutoNode(active=False, operator=None): if tex.use_map_normal: t = TreeNodes.nodes.new('ShaderNodeRGBToBW') t.location = -0, 300 - links.new(t.outputs[0], shout.inputs[2]) - links.new(shtext.outputs[1], t.inputs[0]) + 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") @@ -620,10 +808,21 @@ class mlrefresh(Operator): AutoNodeInitiate(False, self) if CHECK_AUTONODE is True: - bpy.ops.object.editmode_toggle() - bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) - bpy.ops.object.editmode_toggle() + 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'} @@ -641,9 +840,20 @@ class mlrefresh_active(Operator): def execute(self, context): AutoNodeInitiate(True, self) if CHECK_AUTONODE is True: - bpy.ops.object.editmode_toggle() - bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) - bpy.ops.object.editmode_toggle() + 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'} |