From 06a32170e18093f858eaa0e03cf851b632b5d6b0 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez <venomgfx@gmail.com> Date: Sat, 15 Mar 2014 19:38:47 -0300 Subject: [PATCH] Amaranth 0.8.4: - Cycles Sampling, display sampling values for each renderlayers as well. - Rename Missing Node Groups to Node Links, because it can now look for empty node images or images that doesnt exist as well. - Scene Debug: Empty Material Slots, print a list of all the objects that have a empty material slots (common when linked materials got lost) - List Missing Images now show a path if linked from a .blend, clicking on the path will open a new blender instance with that file (same for dupligroups path) Full changelog at: http://pablovazquez.org/amaranth --- scene_amaranth_toolset.py | 360 +++++++++++++++++++++++++++++--------- 1 file changed, 280 insertions(+), 80 deletions(-) diff --git a/scene_amaranth_toolset.py b/scene_amaranth_toolset.py index 6e7a679d..6c34a546 100755 --- a/scene_amaranth_toolset.py +++ b/scene_amaranth_toolset.py @@ -19,7 +19,7 @@ bl_info = { "name": "Amaranth Toolset", "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin", - "version": (0, 8, 2), + "version": (0, 8, 4), "blender": (2, 70), "location": "Everywhere!", "description": "A collection of tools and settings to improve productivity", @@ -1009,7 +1009,7 @@ class MESH_OT_make_symmetric(Operator): return {'FINISHED'} # // FEATURE: Mesh Symmetry Tools by Sergey Sharybin -# FEATURE: Cycles Render Samples per Scene +# FEATURE: Cycles Render Sampling Extra def render_cycles_scene_samples(self, context): layout = self.layout @@ -1018,46 +1018,54 @@ def render_cycles_scene_samples(self, context): scene = context.scene cscene = scene.cycles + col = layout.column(align=True) + + if len(scene.render.layers) == 1 and \ + scene.render.layers[0].samples == 0: + pass + else: + col.separator() + col.label(text="Samples Per RenderLayer:") + + for rl in scene.render.layers: + row = col.row(align=True) + row.label(rl.name) + row.prop(rl, "samples", text="%s" % + "Samples" if rl.samples > 0 else "Samples [Auto]") + if (len(bpy.data.scenes) > 1): - layout.separator() + col.separator() - layout.label(text="Samples Per Scene:") + col.label(text="Samples Per Scene:") + row = col.row(align=True) if cscene.progressive == 'PATH': for s in bpy.data.scenes: if s != scene: + row = col.row(align=True) if s.render.engine == 'CYCLES': cscene = s.cycles - - split = layout.split() - col = split.column() - sub = col.column(align=True) - - sub.label(text="%s" % s.name) - - col = split.column() - sub = col.column(align=True) - sub.prop(cscene, "samples", text="Samples") + + row.label(s.name) + row.prop(cscene, "samples") else: - layout.label(text="Scene: '%s' is not using Cycles" % s.name) + row.label(text="Scene: '%s' is not using Cycles" % s.name) else: for s in bpy.data.scenes: if s != scene: + row = col.row(align=True) if s.render.engine == 'CYCLES': cscene = s.cycles - - split = layout.split() - col = split.column() - sub = col.column(align=True) - - sub.label(text="%s" % s.name) - - col = split.column() - sub = col.column(align=True) - sub.prop(cscene, "aa_samples", text="AA Samples") + + row.label(s.name) + row.prop(cscene, "aa_samples", + text="AA Samples") else: - layout.label(text="Scene: '%s' is not using Cycles" % s.name) -# // FEATURE: Cycles Render Samples per Scene + row.label(text="Scene: '%s' is not using Cycles" % s.name) + +# // FEATURE: Dupli Group Path + +# // FEATURE: Cycles Render Sampling Extra # FEATURE: Motion Paths Extras class POSE_OT_paths_clear_all(Operator): @@ -1195,7 +1203,6 @@ class SCENE_OT_cycles_shader_list_nodes(Operator): """List Cycles materials containing a specific shader""" bl_idname = "scene.cycles_list_nodes" bl_label = "List Materials" - count_ma = 0 materials = [] @classmethod @@ -1205,10 +1212,7 @@ class SCENE_OT_cycles_shader_list_nodes(Operator): def execute(self, context): node_type = context.scene.amaranth_cycles_node_types roughness = False - - # Reset the list and counter self.__class__.materials = [] - self.__class__.count_ma = 0 print("\n=== Cycles Shader Type: %s === \n" % node_type) @@ -1229,8 +1233,7 @@ class SCENE_OT_cycles_shader_list_nodes(Operator): 'not connected', '\n') - self.__class__.materials = list(set(self.__class__.materials)) - self.__class__.count_ma += 1 + self.__class__.materials = sorted(list(set(self.__class__.materials))) if ma.name not in self.__class__.materials: self.__class__.materials.append('%s%s [%s] %s%s' % ( @@ -1239,23 +1242,22 @@ class SCENE_OT_cycles_shader_list_nodes(Operator): '[F]' if ma.use_fake_user else '', ' - [%s]' % roughness if roughness else '')) - if self.__class__.count_ma == 0: + if len(self.__class__.materials) == 0: self.report({"INFO"}, "No materials with nodes type %s found" % node_type) - else: - print("* A total of %s %s using %s was found \n" % ( - self.__class__.count_ma, - "material" if self.__class__.count_ma == 1 else "materials", + print("* A total of %d %s using %s were found \n" % ( + len(self.__class__.materials), + "material" if len(self.__class__.materials) == 1 else "materials", node_type)) count = 0 for mat in self.__class__.materials: - print('%02d. %s' % (count + 1, self.__class__.materials[count])) + print('%02d. %s' % (count+1, self.__class__.materials[count])) count += 1 print("\n") - self.__class__.materials = list(set(self.__class__.materials)) + self.__class__.materials = sorted(list(set(self.__class__.materials))) return {'FINISHED'} @@ -1266,7 +1268,6 @@ class SCENE_OT_cycles_shader_list_nodes_clear(Operator): def execute(self, context): SCENE_OT_cycles_shader_list_nodes.materials[:] = [] - SCENE_OT_cycles_shader_list_nodes.count_ma = 0 print("* Cleared Cycles Materials List") return {'FINISHED'} @@ -1286,56 +1287,183 @@ class SCENE_OT_amaranth_debug_lamp_select(Operator): return{'FINISHED'} -class SCENE_OT_list_missing_node_tree(Operator): - '''Print a list of missing (link lost) node groups''' - bl_idname = "scene.list_missing_node_tree" - bl_label = "List Missing Node Groups" +class SCENE_OT_list_missing_node_links(Operator): + '''Print a list of missing node links''' + bl_idname = "scene.list_missing_node_links" + bl_label = "List Missing Node Links" - count = 0 + count_groups = 0 + count_images = 0 def execute(self, context): - missing = [] + missing_groups = [] + missing_images = [] libraries = [] - self.__class__.count = 0 + self.__class__.count_groups = 0 + self.__class__.count_images = 0 - print("\n* Missing Node Groups\n") for ma in bpy.data.materials: if ma.node_tree: for no in ma.node_tree.nodes: if no.type == 'GROUP': if not no.node_tree: - self.__class__.count += 1 + self.__class__.count_groups += 1 + + users_ngroup = [] - missing.append("%02d. %s%s%s [%s]%s" % ( - self.__class__.count, + for ob in bpy.data.objects: + if ob.material_slots and ma.name in ob.material_slots: + users_ngroup.append("%s%s%s" % ( + "[L] " if ob.library else "", + "[F] " if ob.use_fake_user else "", + ob.name)) + + missing_groups.append("NG: %s%s%s [%s]%s%s\n" % ( "[L] " if ma.library else "", "[F] " if ma.use_fake_user else "", ma.name, ma.users, - "\n %s" % - ma.library.filepath if ma.library else "")) + "\nLI: %s" % + ma.library.filepath if ma.library else "", + "\nOB: %s" % ', '.join(users_ngroup) if users_ngroup else "")) if ma.library: libraries.append(ma.library.filepath) + if no.type == 'TEX_IMAGE': + if no.image: + import os.path + image_path_exists = os.path.exists( + bpy.path.abspath( + no.image.filepath, library=no.image.library)) + + if not no.image or not image_path_exists: + self.__class__.count_images += 1 + + users_images = [] + + for ob in bpy.data.objects: + if ob.material_slots and ma.name in ob.material_slots: + users_images.append("%s%s%s" % ( + "[L] " if ob.library else "", + "[F] " if ob.use_fake_user else "", + ob.name)) + + missing_images.append("MA: %s%s%s [%s]%s%s%s%s\n" % ( + "[L] " if ma.library else "", + "[F] " if ma.use_fake_user else "", + ma.name, ma.users, + "\nLI: %s" % + ma.library.filepath if ma.library else "", + "\nIM: %s" % no.image.name if no.image else "", + "\nLI: %s" % no.image.filepath if no.image and no.image.filepath else "", + "\nOB: %s" % ', '.join(users_images) if users_images else "")) - # Remove duplicates and sort - missing = list(set(missing)) - missing = sorted(missing) - libraries = list(set(libraries)) - - for mi in missing: - print(mi) + if ma.library: + libraries.append(ma.library.filepath) - if missing: - self.report({"INFO"}, "%d missing node %s found! Check console" % - (self.__class__.count, "group" if self.__class__.count == 1 else "groups")) + # Remove duplicates and sort + missing_groups = sorted(list(set(missing_groups))) + missing_images = sorted(list(set(missing_images))) + libraries = sorted(list(set(libraries))) + + print("\n\n== %s missing image %s and %s missing node %s ==" % + ("No" if self.__class__.count_images == 0 else str(self.__class__.count_images), + "node" if self.__class__.count_images == 1 else "nodes", + "no" if self.__class__.count_groups == 0 else str(self.__class__.count_groups), + "group" if self.__class__.count_groups == 1 else "groups")) + + # List Missing Node Groups + if missing_groups: + print("\n* Missing Node Group Links [NG]\n") + for mig in missing_groups: + print(mig) + + # List Missing Image Nodes + if missing_images: + print("\n* Missing Image Nodes Link [IM]\n") + + for mii in missing_images: + print(mii) + + if missing_groups or missing_images: if libraries: - print("\nThat's bad, run this check on %s:" % ( + print("\nThat's bad, run check on %s:" % ( "this library" if len(libraries) == 1 else "these libraries")) for li in libraries: print(li) - print("\n") else: - self.report({"INFO"}, "Yay! No missing node groups") + self.report({"INFO"}, "Yay! No missing node links") + + print("\n") + + if missing_groups and missing_images: + self.report({"WARNING"}, "%d missing image %s and %d missing node %s found" % + (self.__class__.count_images, "node" if self.__class__.count_images == 1 else "nodes", + self.__class__.count_groups, "group" if self.__class__.count_groups == 1 else "groups")) + + return{'FINISHED'} + +class SCENE_OT_list_missing_material_slots(Operator): + '''List objects with empty material slots''' + bl_idname = "scene.list_missing_material_slots" + bl_label = "List Empty Material Slots" + + objects = [] + + def execute(self, context): + self.__class__.objects = [] + + for ob in bpy.data.objects: + for ma in ob.material_slots: + if not ma.material: + self.__class__.objects.append('%s%s' % ( + '[L] ' if ob.library else '', + ob.name)) + self.__class__.objects = sorted(list(set(self.__class__.objects))) + + if len(self.__class__.objects) == 0: + self.report({"INFO"}, "No objects with empty material slots found") + else: + print("\n* A total of %d %s with empty material slots was found \n" % ( + len(self.__class__.objects), + "object" if len(self.__class__.objects) == 1 else "objects")) + + count = 0 + + for obs in self.__class__.objects: + print('%02d. %s' % (count+1, self.__class__.objects[count])) + count += 1 + print("\n") + + return{'FINISHED'} + +class SCENE_OT_list_missing_material_slots_clear(Operator): + """Clear the list below""" + bl_idname = "scene.list_missing_material_slots_clear" + bl_label = "Clear Empty Material Slots List" + + def execute(self, context): + SCENE_OT_list_missing_material_slots.objects[:] = [] + print("* Cleared Empty Material Slots List") + return {'FINISHED'} + +class SCENE_OT_blender_instance_open(Operator): + '''Open in a new Blender instance''' + bl_idname = "scene.blender_instance_open" + bl_label = "Open Blender Instance" + filepath = bpy.props.StringProperty() + + def execute(self, context): + if self.filepath: + filepath = bpy.path.abspath(self.filepath) + + import subprocess + try: + subprocess.Popen([bpy.app.binary_path, filepath]) + except: + print("Error on the new Blender instance") + import traceback + traceback.print_exc() + return{'FINISHED'} class SCENE_PT_scene_debug(Panel): @@ -1356,7 +1484,9 @@ class SCENE_PT_scene_debug(Panel): list_lamps = scene.amaranth_debug_scene_list_lamps list_missing_images = scene.amaranth_debug_scene_list_missing_images materials = SCENE_OT_cycles_shader_list_nodes.materials - materials_count = SCENE_OT_cycles_shader_list_nodes.count_ma + materials_count = len(SCENE_OT_cycles_shader_list_nodes.materials) + missing_material_slots_obs = SCENE_OT_list_missing_material_slots.objects + missing_material_slots_count = len(SCENE_OT_list_missing_material_slots.objects) engine = scene.render.engine # List Lamps @@ -1488,7 +1618,8 @@ class SCENE_PT_scene_debug(Panel): '[L] ' if im.library else '', im.name, im.users, ' [F]' if im.use_fake_user else ''), - im.filepath if im.filepath else 'No Filepath']) + im.filepath if im.filepath else 'No Filepath', + im.library.filepath if im.library else '']) if images_missing: row = col.row(align=True) @@ -1510,7 +1641,14 @@ class SCENE_PT_scene_debug(Panel): for mis in images_missing: col.label(text=mis[0], icon="IMAGE_DATA") - col.label(text=mis[1], icon="BLANK1") + col.label(text=mis[1], icon="LIBRARY_DATA_DIRECT") + if mis[2]: + row = col.row(align=True) + row.alignment = "LEFT" + row.operator(SCENE_OT_blender_instance_open.bl_idname, + text=mis[2], + icon="LINK_BLEND", + emboss=False).filepath=mis[2] col.separator() else: row = col.row(align=True) @@ -1552,6 +1690,7 @@ class SCENE_PT_scene_debug(Panel): pass else: if materials_count != 0: + col = box.column(align=True) count = 0 col.label(text="%s %s found" % (materials_count, 'material' if materials_count == 1 else 'materials'), icon="INFO") @@ -1560,25 +1699,83 @@ class SCENE_PT_scene_debug(Panel): col.label(text='%s' % (materials[count-1]), icon="MATERIAL") # List Missing Node Trees - split = layout.split() - split.label(text="Missing Node Groups") - split.operator(SCENE_OT_list_missing_node_tree.bl_idname, + box = layout.box() + row = box.row(align=True) + split = row.split() + col = split.column(align=True) + + split = col.split() + split.label(text="Node Links") + split.operator(SCENE_OT_list_missing_node_links.bl_idname, icon="NODETREE") - if SCENE_OT_list_missing_node_tree.count != 0: - layout.label(text="%s" % ("%s missing %s found, check the console!" % ( - str(SCENE_OT_list_missing_node_tree.count), - "group" if SCENE_OT_list_missing_node_tree.count == 1 else "groups")), - icon="ERROR") + if SCENE_OT_list_missing_node_links.count_groups != 0 or \ + SCENE_OT_list_missing_node_links.count_images != 0: + col.label(text="Warning! Check Console", icon="ERROR") + + if SCENE_OT_list_missing_node_links.count_groups != 0: + col.label(text="%s" % ("%s node %s missing link" % ( + str(SCENE_OT_list_missing_node_links.count_groups), + "group" if SCENE_OT_list_missing_node_links.count_groups == 1 else "groups")), + icon="NODETREE") + if SCENE_OT_list_missing_node_links.count_images != 0: + col.label(text="%s" % ("%s image %s missing link" % ( + str(SCENE_OT_list_missing_node_links.count_images), + "node" if SCENE_OT_list_missing_node_links.count_images == 1 else "nodes")), + icon="IMAGE_DATA") + + col.separator() + + # List Empty Materials Slots + box = layout.box() + split = box.split() + col = split.column(align=True) + col.label(text="Material Slots") + + row = split.row(align=True) + row.operator(SCENE_OT_list_missing_material_slots.bl_idname, + icon="MATERIAL", + text="List Empty Materials Slots") + if missing_material_slots_count != 0: + row.operator(SCENE_OT_list_missing_material_slots_clear.bl_idname, + icon="X", text="") + col.separator() + + try: + missing_material_slots_obs + except NameError: + pass + else: + if missing_material_slots_count != 0: + col = box.column(align=True) + count = 0 + col.label(text="%s %s with empty material slots found" % ( + missing_material_slots_count, + 'object' if missing_material_slots_count == 1 else 'objects'), + icon="INFO") + + for obs in missing_material_slots_obs: + count += 1 + col.label(text='%s' % ( + missing_material_slots_obs[count-1]), + icon="OBJECT_DATA") # // FEATURE: Scene Debug # FEATURE: Dupli Group Path def ui_dupli_group_library_path(self, context): ob = context.object + lib = ob.dupli_group.library.filepath + + row = self.layout.row() + row.alignment = 'LEFT' if ob and ob.dupli_group and ob.dupli_group.library: - self.layout.label(text="Library: %s" % ob.dupli_group.library.filepath) + row.operator(SCENE_OT_blender_instance_open.bl_idname, + text="Library: %s" % lib, + emboss=False, + icon="LINK_BLEND").filepath=lib + # // FEATURE: Dupli Group Path # FEATURE: Color Management Presets class SCENE_MT_color_management_presets(Menu): @@ -1652,7 +1849,10 @@ classes = (SCENE_MT_color_management_presets, SCENE_OT_cycles_shader_list_nodes, SCENE_OT_cycles_shader_list_nodes_clear, SCENE_OT_amaranth_debug_lamp_select, - SCENE_OT_list_missing_node_tree, + SCENE_OT_list_missing_node_links, + SCENE_OT_list_missing_material_slots, + SCENE_OT_list_missing_material_slots_clear, + SCENE_OT_blender_instance_open, WM_OT_save_reload, MESH_OT_find_asymmetric, MESH_OT_make_symmetric, -- GitLab