Skip to content
Snippets Groups Projects
scene_amaranth_toolset.py 116 KiB
Newer Older
  • Learn to ignore specific revisions
  •         else:
                space.show_only_render = True
            return {'FINISHED'}
    
    
    # FEATURE: Display Active Image Node on Image Editor
    # Made by Sergey Sharybin, tweaks from Bassam Kurdali
    image_nodes = {"CompositorNodeImage",
                   "ShaderNodeTexImage",
                   "ShaderNodeTexEnvironment"}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_OT_show_active_node_image(Operator):
    
        """Show active image node image in the image editor"""
        bl_idname = "node.show_active_node_image"
        bl_label = "Show Active Node Node"
        bl_options = {'UNDO'}
    
        def execute(self, context):
            preferences = context.user_preferences.addons[__name__].preferences
            if preferences.use_image_node_display:
                if context.active_node:
                    active_node = context.active_node
                    if active_node.bl_idname in image_nodes and active_node.image:
                        for area in context.screen.areas:
                            if area.type == "IMAGE_EDITOR":
                                for space in area.spaces:
                                    if space.type == "IMAGE_EDITOR":
                                        space.image = active_node.image
                                break
    
            return {'FINISHED'}
    # // FEATURE: Display Active Image Node on Image Editor
    
    # FEATURE: Select Meshlights
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_OBJECT_OT_select_meshlights(Operator):
    
        """Select light emitting meshes"""
        bl_idname = "object.select_meshlights"
        bl_label = "Select Meshlights"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(cls, context):
            return context.scene.render.engine == 'CYCLES'
    
        def execute(self, context):
            # Deselect everything first
            bpy.ops.object.select_all(action='DESELECT')
    
            for ob in context.scene.objects:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                if cycles_is_emission(context, ob):
                    ob.select = True
                    context.scene.objects.active = ob
    
    
            if not context.selected_objects and not context.scene.objects.active:
                self.report({'INFO'}, "No meshlights to select")
    
            return {'FINISHED'}
    
    def button_select_meshlights(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        if cycles_exists and context.scene.render.engine == 'CYCLES':
    
            self.layout.operator('object.select_meshlights', icon="LAMP_SUN")
    # // FEATURE: Select Meshlights
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # FEATURE: Mesh Symmetry Tools by Sergey Sharybin
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_MESH_OT_find_asymmetric(Operator):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        """
        Find asymmetric vertices
        """
    
        bl_idname = "mesh.find_asymmetric"
        bl_label = "Find Asymmetric"
        bl_options = {'UNDO', 'REGISTER'}
    
        @classmethod
        def poll(cls, context):
            object = context.object
            if object:
                return object.mode == 'EDIT' and object.type == 'MESH'
            return False
    
        def execute(self, context):
            threshold = 1e-6
    
            object = context.object
            bm = bmesh.from_edit_mesh(object.data)
    
            # Deselect all the vertices
            for v in bm.verts:
                v.select = False
    
            for v1 in bm.verts:
                if abs(v1.co[0]) < threshold:
                    continue
    
                mirror_found = False
                for v2 in bm.verts:
                    if v1 == v2:
                        continue
                    if v1.co[0] * v2.co[0] > 0.0:
                        continue
    
                    mirror_coord = Vector(v2.co)
                    mirror_coord[0] *= -1
                    if (mirror_coord - v1.co).length_squared < threshold:
                        mirror_found = True
                        break
                if not mirror_found:
                    v1.select = True
    
            bm.select_flush_mode()
    
            bmesh.update_edit_mesh(object.data)
    
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_MESH_OT_make_symmetric(Operator):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        """
        Make symmetric
        """
    
        bl_idname = "mesh.make_symmetric"
        bl_label = "Make Symmetric"
        bl_options = {'UNDO', 'REGISTER'}
    
        @classmethod
        def poll(cls, context):
            object = context.object
            if object:
                return object.mode == 'EDIT' and object.type == 'MESH'
            return False
    
        def execute(self, context):
            threshold = 1e-6
    
            object = context.object
            bm = bmesh.from_edit_mesh(object.data)
    
            for v1 in bm.verts:
                if v1.co[0] < threshold:
                    continue
                if not v1.select:
                    continue
    
                closest_vert = None
                closest_distance = -1
                for v2 in bm.verts:
                    if v1 == v2:
                        continue
                    if v2.co[0] > threshold:
                        continue
                    if not v2.select:
                        continue
    
                    mirror_coord = Vector(v2.co)
                    mirror_coord[0] *= -1
                    distance = (mirror_coord - v1.co).length_squared
                    if closest_vert is None or distance < closest_distance:
                        closest_distance = distance
                        closest_vert = v2
    
                if closest_vert:
                    closest_vert.select = False
                    closest_vert.co = Vector(v1.co)
                    closest_vert.co[0] *= -1
                v1.select = False
    
            for v1 in bm.verts:
                if v1.select:
                    closest_vert = None
                    closest_distance = -1
                    for v2 in bm.verts:
                        if v1 != v2:
                            mirror_coord = Vector(v2.co)
                            mirror_coord[0] *= -1
                            distance = (mirror_coord - v1.co).length_squared
                            if closest_vert is None or distance < closest_distance:
                                closest_distance = distance
                                closest_vert = v2
                    if closest_vert:
                        v1.select = False
                        v1.co = Vector(closest_vert.co)
                        v1.co[0] *= -1
    
            bm.select_flush_mode()
            bmesh.update_edit_mesh(object.data)
    
            return {'FINISHED'}
    # // FEATURE: Mesh Symmetry Tools by Sergey Sharybin
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # FEATURE: Cycles Render Sampling Extra
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    def render_cycles_scene_samples(self, context):
    
        layout = self.layout
        scenes = bpy.data.scenes
        scene = context.scene
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        render = scene.render
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        if cycles_exists:
            cscene = scene.cycles
            list_sampling = scene.amaranth_cycles_list_sampling
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        # Set Render Samples
        if cycles_exists and cscene.progressive == 'BRANCHED_PATH':
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            layout.separator()
            split = layout.split()
            col = split.column()
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            col.operator(
                AMTH_RENDER_OT_cycles_samples_percentage_set.bl_idname,
                text="%s" % 'Set as Render Samples' if cscene.use_samples_final else 'Set New Render Samples',
                icon="%s" % 'PINNED' if cscene.use_samples_final else 'UNPINNED')
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            col = split.column()
            row = col.row(align=True)
            row.enabled = True if scene.get('amth_cycles_samples_final') else False
    
            row.operator(
                AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
                text="100%").percent=100
            row.operator(
                AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
                text="75%").percent=75
            row.operator(
                AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
                text="50%").percent=50
            row.operator(
                AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
                text="25%").percent=25
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        # List Samples
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        if (len(scene.render.layers) > 1) or \
            (len(bpy.data.scenes) > 1):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            box = layout.box()
            row = box.row(align=True)
            col = row.column(align=True)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            row = col.row(align=True)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            row.alignment = 'LEFT'
            row.prop(scene, 'amaranth_cycles_list_sampling',
                        icon="%s" % 'TRIA_DOWN' if list_sampling else 'TRIA_RIGHT',
                        emboss=False)
    
        if list_sampling:
            if len(scene.render.layers) == 1 and \
                render.layers[0].samples == 0:
                pass
            else:
                col.separator()
                col.label(text="RenderLayers:", icon='RENDERLAYERS')
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                for rl in scene.render.layers:
                    row = col.row(align=True)
                    row.label(rl.name, icon='BLANK1')
                    row.prop(rl, "samples", text="%s" %
                        "Samples" if rl.samples > 0 else "Automatic (%s)" % (
                            cscene.aa_samples if cscene.progressive == 'BRANCHED_PATH' else cscene.samples))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if (len(bpy.data.scenes) > 1):
                col.separator()
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                col.label(text="Scenes:", icon='SCENE_DATA')
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                if cycles_exists and cscene.progressive == 'PATH':
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    for s in bpy.data.scenes:
                        if s != scene:
                            row = col.row(align=True)
                            if s.render.engine == 'CYCLES':
                                cscene = s.cycles
    
                                row.label(s.name)
                                row.prop(cscene, "samples", icon='BLANK1')
                            else:
                                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
    
                                row.label(s.name, icon='BLANK1')
                                row.prop(cscene, "aa_samples",
                                    text="AA Samples")
                            else:
                                row.label(text="Scene: '%s' is not using Cycles" % s.name)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
    # // FEATURE: Cycles Render Sampling Extra
    
    # FEATURE: Motion Paths Extras
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_POSE_OT_paths_clear_all(Operator):
    
        """Clear motion paths from all bones"""
        bl_idname = "pose.paths_clear_all"
        bl_label = "Clear All Motion Paths"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(cls, context):
            return context.mode == 'POSE'
    
        def execute(self, context):
            #silly but works
            for b in context.object.data.bones:
                b.select = True
                bpy.ops.pose.paths_clear()
                b.select = False
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_POSE_OT_paths_frame_match(Operator):
    
        """Match Start/End frame of scene to motion path range"""
        bl_idname = "pose.paths_frame_match"
        bl_label = "Match Frame Range"
        bl_options = {'UNDO'}
    
        def execute(self, context):
            avs = context.object.pose.animation_visualization
            scene = context.scene
    
            if avs.motion_path.type == 'RANGE':
                if scene.use_preview_range:
                    avs.motion_path.frame_start = scene.frame_preview_start
                    avs.motion_path.frame_end = scene.frame_preview_end
                else:
                    avs.motion_path.frame_start = scene.frame_start
                    avs.motion_path.frame_end = scene.frame_end
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            else:
    
                if scene.use_preview_range:
                    avs.motion_path.frame_before = scene.frame_preview_start
                    avs.motion_path.frame_after = scene.frame_preview_end
                else:
                    avs.motion_path.frame_before = scene.frame_start
                    avs.motion_path.frame_after = scene.frame_end
    
            return {'FINISHED'}
    
    def pose_motion_paths_ui(self, context):
    
        layout = self.layout
        scene = context.scene
        avs = context.object.pose.animation_visualization
        if context.active_pose_bone:
            mpath = context.active_pose_bone.motion_path
        layout.separator()    
        layout.label(text="Motion Paths Extras:")
    
        split = layout.split()
    
        col = split.column(align=True)
    
        if context.selected_pose_bones:
            if mpath:
                sub = col.row(align=True)
                sub.operator("pose.paths_update", text="Update Path", icon='BONE_DATA')
                sub.operator("pose.paths_clear", text="", icon='X')
            else:
                col.operator("pose.paths_calculate", text="Calculate Path", icon='BONE_DATA')
        else:
            col.label(text="Select Bones First", icon="ERROR")
    
        col = split.column(align=True)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        col.operator(AMTH_POSE_OT_paths_frame_match.bl_idname,
    
            text="{}".format( "Set Preview Frame Range"
                    if scene.use_preview_range else "Set Frame Range"),
            icon="{}".format("PREVIEW_RANGE"
                    if scene.use_preview_range else "TIME"))
    
        col = layout.column()
        row = col.row(align=True)
    
        if avs.motion_path.type == 'RANGE':
            row.prop(avs.motion_path, "frame_start", text="Start")
            row.prop(avs.motion_path, "frame_end", text="End")
        else:
            row.prop(avs.motion_path, "frame_before", text="Before")
            row.prop(avs.motion_path, "frame_after", text="After")
    
        layout.separator()
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        layout.operator(AMTH_POSE_OT_paths_clear_all.bl_idname, icon="X")
    
    # // FEATURE: Motion Paths Extras
    
    # FEATURE: Final Render Resolution Display
    def render_final_resolution_ui(self, context):
    
        rd = context.scene.render
        layout = self.layout
    
        final_res_x = (rd.resolution_x * rd.resolution_percentage) / 100
        final_res_y = (rd.resolution_y * rd.resolution_percentage) / 100
    
        if rd.use_border:
           final_res_x_border = round((final_res_x * (rd.border_max_x - rd.border_min_x)))
           final_res_y_border = round((final_res_y * (rd.border_max_y - rd.border_min_y)))
           layout.label(text="Final Resolution: {} x {} [Border: {} x {}]".format(
                 str(final_res_x)[:-2], str(final_res_y)[:-2],
                 str(final_res_x_border), str(final_res_y_border)))
        else:
            layout.label(text="Final Resolution: {} x {}".format(
                 str(final_res_x)[:-2], str(final_res_y)[:-2]))
    # // FEATURE: Final Render Resolution Display
    
    # FEATURE: Shader Nodes Extra Info
    def node_shader_extra(self, context):
    
        if context.space_data.tree_type == 'ShaderNodeTree':
            ob = context.active_object
            snode = context.space_data
            layout = self.layout
    
            if ob and snode.shader_type != 'WORLD':
                if ob.type == 'LAMP':
                    layout.label(text="%s" % ob.name,
                                 icon="LAMP_%s" % ob.data.type)        
                else:
                    layout.label(text="%s" % ob.name,
                                 icon="OUTLINER_DATA_%s" % ob.type)
                 
    
    # // FEATURE: Shader Nodes Extra Info
    
    # FEATURE: Scene Debug
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_cycles_shader_list_nodes(Operator):
    
        """List Cycles materials containing a specific shader"""
        bl_idname = "scene.cycles_list_nodes"
        bl_label = "List Materials"
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        materials = []
    
    
        @classmethod
        def poll(cls, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            return cycles_exists and context.scene.render.engine == 'CYCLES'
    
    
        def execute(self, context):
            node_type = context.scene.amaranth_cycles_node_types
            roughness = False
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.materials = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            shaders_roughness = ['BSDF_GLOSSY','BSDF_DIFFUSE','BSDF_GLASS']
    
    
            print("\n=== Cycles Shader Type: %s === \n" % node_type)
    
            for ma in bpy.data.materials:
                if ma.node_tree:
                    nodes = ma.node_tree.nodes
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    
                    print_unconnected = ('Note: \nOutput from "%s" node' % node_type,
                                            'in material "%s"' % ma.name, 'not connected\n')
    
    
                    for no in nodes:
                        if no.type == node_type:
                            for ou in no.outputs:
                                if ou.links:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    connected = True
                                    if no.type in shaders_roughness:
    
                                        roughness = 'R: %.4f' % no.inputs['Roughness'].default_value
                                    else:
                                        roughness = False
                                else:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    connected = False
                                    print(print_unconnected)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                if ma.name not in self.__class__.materials:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    self.__class__.materials.append('%s%s [%s] %s%s%s' % (
    
                                        '[L] ' if ma.library else '',
                                        ma.name, ma.users,
                                        '[F]' if ma.use_fake_user else '',
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        ' - [%s]' % roughness if roughness else '',
                                        ' * Output not connected' if not connected else ''))
    
                        elif no.type == 'GROUP':
                            if no.node_tree:
                                for nog in no.node_tree.nodes:
                                    if nog.type == node_type:
                                        for ou in nog.outputs:
                                            if ou.links:
                                                connected = True
                                                if nog.type in shaders_roughness:
                                                    roughness = 'R: %.4f' % nog.inputs['Roughness'].default_value
                                                else:
                                                    roughness = False
                                            else:
                                                connected = False
                                                print(print_unconnected)
    
                                            if ma.name not in self.__class__.materials:
                                                self.__class__.materials.append('%s%s%s [%s] %s%s%s' % (
                                                    '[L] ' if ma.library else '',
                                                    'Node Group:  %s%s  ->  ' % (
                                                        '[L] ' if no.node_tree.library else '',
                                                        no.node_tree.name),
                                                    ma.name, ma.users,
                                                    '[F]' if ma.use_fake_user else '',
                                                    ' - [%s]' % roughness if roughness else '',
                                                    ' * Output not connected' if not connected else ''))
    
                        self.__class__.materials = sorted(list(set(self.__class__.materials)))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if len(self.__class__.materials) == 0:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                self.report({"INFO"}, "No materials with nodes type %s found" % node_type)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                print("* A total of %d %s using %s was found \n" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        len(self.__class__.materials),
                        "material" if len(self.__class__.materials) == 1 else "materials",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        node_type))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                for mat in self.__class__.materials:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    print('%02d. %s' % (count+1, self.__class__.materials[count]))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    count += 1
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.materials = sorted(list(set(self.__class__.materials)))
    
    
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_cycles_shader_list_nodes_clear(Operator):
    
        """Clear the list below"""
        bl_idname = "scene.cycles_list_nodes_clear"
        bl_label = "Clear Materials List"
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
        @classmethod
        def poll(cls, context):
            return cycles_exists
    
    
        def execute(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            AMTH_SCENE_OT_cycles_shader_list_nodes.materials[:] = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            print("* Cleared Cycles Materials List")
    
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_amaranth_object_select(Operator):
        '''Select object'''
        bl_idname = "scene.amaranth_object_select"
        bl_label = "Select Object"
        object = bpy.props.StringProperty()
    
     
        def execute(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if self.object:
                object = bpy.data.objects[self.object]
    
    
                bpy.ops.object.select_all(action='DESELECT')
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                object.select = True
                context.scene.objects.active = object
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            return{'FINISHED'}
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_list_missing_node_links(Operator):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        '''Print a list of missing node links'''
        bl_idname = "scene.list_missing_node_links"
        bl_label = "List Missing Node Links"
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        count_groups = 0
        count_images = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        count_image_node_unlinked = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
        def execute(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            missing_groups = []
            missing_images = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            image_nodes_unlinked = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            libraries = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.count_groups = 0
            self.__class__.count_images = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.count_image_node_unlinked = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
            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:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                self.__class__.count_groups += 1
    
                                users_ngroup = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                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))
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                missing_groups.append("MA: %s%s%s [%s]%s%s%s\n" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    "[L] " if ma.library else "",
                                    "[F] " if ma.use_fake_user else "",
                                    ma.name, ma.users,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    " *** No users *** " if ma.users == 0 else "",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    "\nLI: %s" % 
                                    ma.library.filepath if ma.library else "",
                                    "\nOB: %s" % ',  '.join(users_ngroup) if users_ngroup else ""))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                                if ma.library:
                                    libraries.append(ma.library.filepath)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        if no.type == 'TEX_IMAGE':
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                            outputs_empty = not no.outputs['Color'].is_linked and not no.outputs['Alpha'].is_linked
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                            if no.image:
                                import os.path
                                image_path_exists = os.path.exists(
                                                        bpy.path.abspath(
                                                            no.image.filepath, library=no.image.library))
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                            if outputs_empty or not \
                               no.image or not \
                               image_path_exists:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                                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))
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                if outputs_empty:
                                    self.__class__.count_image_node_unlinked += 1
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    image_nodes_unlinked.append("%s%s%s%s%s [%s]%s%s%s%s%s\n" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        "NO: %s" % no.name,
                                        "\nMA: ",
                                        "[L] " if ma.library else "",
                                        "[F] " if ma.use_fake_user else "",
                                        ma.name, ma.users,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        " *** No users *** " if ma.users == 0 else "",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        "\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 ""))
                                
    
                                if not no.image or not image_path_exists:
                                    self.__class__.count_images += 1
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    missing_images.append("MA: %s%s%s [%s]%s%s%s%s%s\n" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        "[L] " if ma.library else "",
                                        "[F] " if ma.use_fake_user else "",
                                        ma.name, ma.users,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        " *** No users *** " if ma.users == 0 else "",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                        "\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 ""))
    
                                    if ma.library:
                                        libraries.append(ma.library.filepath)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            # Remove duplicates and sort
            missing_groups = sorted(list(set(missing_groups)))
            missing_images = sorted(list(set(missing_images)))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            image_nodes_unlinked = sorted(list(set(image_nodes_unlinked)))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            libraries = sorted(list(set(libraries)))
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            print("\n\n== %s missing image %s, %s missing node %s and %s image %s unlinked ==" %
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                ("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),
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                "group" if self.__class__.count_groups == 1 else "groups",
                "no" if self.__class__.count_image_node_unlinked == 0 else str(self.__class__.count_image_node_unlinked),
                "node" if self.__class__.count_groups == 1 else "nodes"))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
            # List Missing Node Groups
            if missing_groups:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                print("\n* Missing Node Group Links\n")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                for mig in missing_groups:
                    print(mig)
    
            # List Missing Image Nodes
            if missing_images:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                print("\n* Missing Image Nodes Link\n")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                for mii in missing_images:
                    print(mii)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            # List Image Nodes with its outputs unlinked
            if image_nodes_unlinked:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                print("\n* Image Nodes Unlinked\n")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                for nou in image_nodes_unlinked:
                    print(nou)
    
            if missing_groups or \
               missing_images or \
               image_nodes_unlinked:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                if libraries:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    print("\nThat's bad, run check on %s:" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        "this library" if len(libraries) == 1 else "these libraries"))
                    for li in libraries:
                        print(li)
            else:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                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'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_list_missing_material_slots(Operator):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        '''List objects with empty material slots'''
        bl_idname = "scene.list_missing_material_slots"
        bl_label = "List Empty Material Slots"
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        objects = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        libraries = []
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        def execute(self, context):
            self.__class__.objects = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.libraries = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
            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))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        if ob.library:
                            self.__class__.libraries.append(ob.library.filepath)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.objects = sorted(list(set(self.__class__.objects)))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            self.__class__.libraries = sorted(list(set(self.__class__.libraries)))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
            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
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                count_lib = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                for obs in self.__class__.objects:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    print('%02d. %s' % (
                        count+1, self.__class__.objects[count]))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    count += 1
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                if self.__class__.libraries:
                    print("\n\n* Check %s:\n" % 
                        ("this library" if len(self.__class__.libraries) == 1
                            else "these libraries"))
    
                    for libs in self.__class__.libraries:
                        print('%02d. %s' % (
                            count_lib+1, self.__class__.libraries[count_lib]))
                        count_lib += 1
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                print("\n")
    
            return{'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_list_missing_material_slots_clear(Operator):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        """Clear the list below"""
        bl_idname = "scene.list_missing_material_slots_clear"
        bl_label = "Clear Empty Material Slots List"
        
        def execute(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            AMTH_SCENE_OT_list_missing_material_slots.objects[:] = []
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            print("* Cleared Empty Material Slots List")
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_blender_instance_open(Operator):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        '''Open in a new Blender instance'''
        bl_idname = "scene.blender_instance_open"
        bl_label = "Open Blender Instance"
        filepath = bpy.props.StringProperty()
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        def execute(self, context):
            if self.filepath:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                import os.path
                filepath = os.path.normpath(bpy.path.abspath(self.filepath))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                import subprocess
                try:
                    subprocess.Popen([bpy.app.binary_path, filepath])
                except:
                    print("Error on the new Blender instance")
                    import traceback
                    traceback.print_exc()
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            return{'FINISHED'}
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_PT_scene_debug(Panel):
    
        '''Scene Debug'''
        bl_label = 'Scene Debug'
        bl_space_type = "PROPERTIES"
        bl_region_type = "WINDOW"
        bl_context = "scene"
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        def draw_header(self, context):
            layout = self.layout
            layout.label(text="", icon="RADIO")
    
    
        def draw(self, context):
            layout = self.layout
            scene = context.scene
            objects =  bpy.data.objects
            ob_act = context.active_object
            images = bpy.data.images
            lamps = bpy.data.lamps
            images_missing = []
            list_missing_images = scene.amaranth_debug_scene_list_missing_images
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            materials = AMTH_SCENE_OT_cycles_shader_list_nodes.materials
            materials_count = len(AMTH_SCENE_OT_cycles_shader_list_nodes.materials)
            missing_material_slots_obs = AMTH_SCENE_OT_list_missing_material_slots.objects
            missing_material_slots_count = len(AMTH_SCENE_OT_list_missing_material_slots.objects)
            missing_material_slots_lib = AMTH_SCENE_OT_list_missing_material_slots.libraries
    
            engine = scene.render.engine
    
            # List Missing Images
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            box = layout.box()
            row = box.row(align=True)
            split = row.split()
            col = split.column()
    
    
            if images:
                import os.path
    
                for im in images:
                    if im.type not in ['UV_TEST', 'RENDER_RESULT', 'COMPOSITING']: 
                        if not os.path.exists(bpy.path.abspath(im.filepath, library=im.library)):
                            images_missing.append(["%s%s [%s]%s" % (
                                '[L] ' if im.library else '',
                                im.name, im.users,
                                ' [F]' if im.use_fake_user else ''),
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                im.filepath if im.filepath else 'No Filepath',
                                im.library.filepath if im.library else ''])
    
    
                if images_missing:
                    row = col.row(align=True)
                    row.alignment = 'LEFT'
                    row.prop(scene, 'amaranth_debug_scene_list_missing_images',
                                icon="%s" % 'TRIA_DOWN' if list_missing_images else 'TRIA_RIGHT',
                                emboss=False)
    
                    split = split.split()
                    col = split.column()
    
                    col.label(text="%s missing %s" % (
                                 str(len(images_missing)),
                                 'image' if len(images_missing) == 1 else 'images'),
                                 icon="ERROR")
    
                    if list_missing_images:
                        col = box.column(align=True)
                        for mis in images_missing:
                            col.label(text=mis[0],
                             icon="IMAGE_DATA")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                            col.label(text=mis[1], icon="LIBRARY_DATA_DIRECT")
                            if mis[2]:
                                row = col.row(align=True)
                                row.alignment = "LEFT"
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                             text=mis[2],
                                             icon="LINK_BLEND",
                                             emboss=False).filepath=mis[2]
    
                            col.separator()
                else:
                    row = col.row(align=True)
                    row.alignment = 'LEFT'
                    row.label(text="Great! No missing images", icon="RIGHTARROW_THIN")
    
                    split = split.split()
                    col = split.column()
    
                    col.label(text="%s %s loading correctly" % (
                                 str(len(images)),
                                 'image' if len(images) == 1 else 'images'),
                                 icon="IMAGE_DATA")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            else:
                row = col.row(align=True)
                row.alignment = 'LEFT'
                row.label(text="No images loaded yet", icon="RIGHTARROW_THIN")
    
            # List Cycles Materials by Shader
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if cycles_exists and engine == 'CYCLES':
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                box = layout.box()
                split = box.split()
                col = split.column(align=True)
                col.prop(scene, 'amaranth_cycles_node_types',
                    icon="MATERIAL")
    
                row = split.row(align=True)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                row.operator(AMTH_SCENE_OT_cycles_shader_list_nodes.bl_idname,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                icon="SORTSIZE",
                                text="List Materials Using Shader")
                if materials_count != 0: 
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    row.operator(AMTH_SCENE_OT_cycles_shader_list_nodes_clear.bl_idname,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                    icon="X", text="")
                col.separator()
    
                try:
                    materials
                except NameError:
                    pass
                else:
                    if materials_count != 0: 
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        col = box.column(align=True)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        count = 0
                        col.label(text="%s %s found" % (materials_count,
                            'material' if materials_count == 1 else 'materials'), icon="INFO")
                        for mat in materials:
                            count += 1
                            col.label(text='%s' % (materials[count-1]), icon="MATERIAL")
    
            # List Missing Node Trees
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            box = layout.box()
            row = box.row(align=True)
            split = row.split()
            col = split.column(align=True)
    
            split = col.split()
            split.label(text="Node Links")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            split.operator(AMTH_SCENE_OT_list_missing_node_links.bl_idname,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                            icon="NODETREE")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if AMTH_SCENE_OT_list_missing_node_links.count_groups != 0 or \
                AMTH_SCENE_OT_list_missing_node_links.count_images != 0 or \
                AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked != 0:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                col.label(text="Warning! Check Console", icon="ERROR")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if AMTH_SCENE_OT_list_missing_node_links.count_groups != 0:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                col.label(text="%s" % ("%s node %s missing link" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                         str(AMTH_SCENE_OT_list_missing_node_links.count_groups),
                         "group" if AMTH_SCENE_OT_list_missing_node_links.count_groups == 1 else "groups")),
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                         icon="NODETREE")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if AMTH_SCENE_OT_list_missing_node_links.count_images != 0:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                col.label(text="%s" % ("%s image %s missing link" % (
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                         str(AMTH_SCENE_OT_list_missing_node_links.count_images),
                         "node" if AMTH_SCENE_OT_list_missing_node_links.count_images == 1 else "nodes")),
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                         icon="IMAGE_DATA")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked != 0:
                col.label(text="%s" % ("%s image %s with no output conected" % (
                         str(AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked),
                         "node" if AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked == 1 else "nodes")),
                         icon="NODE")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
            # 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)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            row.operator(AMTH_SCENE_OT_list_missing_material_slots.bl_idname,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                            icon="MATERIAL",
                            text="List Empty Materials Slots")
            if missing_material_slots_count != 0: 
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                row.operator(AMTH_SCENE_OT_list_missing_material_slots_clear.bl_idname,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                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
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    count_lib = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    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
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
                        row = col.row()
                        row.alignment = 'LEFT'
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        row.label(text='%s' % missing_material_slots_obs[count-1],
                                    icon="OBJECT_DATA")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                    if missing_material_slots_lib:
                        col.separator()
                        col.label("Check %s:" % (
                            "this library" if
                                len(missing_material_slots_lib) == 1
                                    else "these libraries"))
                        
                        for libs in missing_material_slots_lib:
                            count_lib += 1
                            row = col.row(align=True)
                            row.alignment = "LEFT"
                            row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname,
                                         text=missing_material_slots_lib[count_lib-1],
                                         icon="LINK_BLEND",
                                         emboss=False).filepath=missing_material_slots_lib[count_lib-1]
    
    
    # // FEATURE: Scene Debug
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # FEATURE: Dupli  Group Path
    def ui_dupli_group_library_path(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        ob = context.object
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
        row = self.layout.row()
        row.alignment = 'LEFT'