Amaranth 0.8.9 Update
authorPablo Vazquez <venomgfx@gmail.com>
Wed, 16 Apr 2014 03:10:20 +0000 (00:10 -0300)
committerPablo Vazquez <venomgfx@gmail.com>
Wed, 16 Apr 2014 03:10:20 +0000 (00:10 -0300)
New:
- New Feature: Specify how many frames to jump on Shift+Up/Down.
  Find it on the Editing tab, User Preferences. Hit "Save Settings" will save
  the setting on your user preferences.

- Tweak/Fix: "Select Meshlights" and meshlights counter on info header was missing
  Emission nodes that were inside groups (not very likely to happen but still).
  Now take them into account. Also made it into a function to reuse.

Scene Debug:
- Tweak: Made "select lamp" operator more generic, now it selects any object
  passed as argument. Reason was to use it on Empty Material Slots
  list as well. Click on OB name to select.
- Display a more obvious remark when images listed have no users.

Fix: Blender new instance operator now will always open with real absolute paths,
  it could mess with texture/image paths sometimes if remapped+saved.

scene_amaranth_toolset.py

index f251bdb..e324b9e 100755 (executable)
@@ -19,7 +19,7 @@
 bl_info = {
     "name": "Amaranth Toolset",
     "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin",
 bl_info = {
     "name": "Amaranth Toolset",
     "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin",
-    "version": (0, 8, 8),
+    "version": (0, 8, 9),
     "blender": (2, 70),
     "location": "Everywhere!",
     "description": "A collection of tools and settings to improve productivity",
     "blender": (2, 70),
     "location": "Everywhere!",
     "description": "A collection of tools and settings to improve productivity",
@@ -74,6 +74,12 @@ class AmaranthToolsetPreferences(AddonPreferences):
             default=True,
             )
 
             default=True,
             )
 
+    frames_jump = IntProperty(
+                name="Frames",
+                description="Number of frames to jump forward/backward",
+                default=10,
+                min=1)
+
 
     def draw(self, context):
         layout = self.layout
 
     def draw(self, context):
         layout = self.layout
@@ -205,7 +211,6 @@ def init_properties():
         description="Use current shader samples as final render samples",
         default=False,)
 
         description="Use current shader samples as final render samples",
         default=False,)
 
-
 def clear_properties():
     props = (
         "use_unsimplify_render",
 def clear_properties():
     props = (
         "use_unsimplify_render",
@@ -255,6 +260,31 @@ def amaranth_text_startup(context):
     except AttributeError:
         return None
 
     except AttributeError:
         return None
 
+# Is Emission Material? For select and stats
+def cycles_is_emission(context, ob):
+
+    is_emission = False
+
+    if ob.material_slots:
+        for ma in ob.material_slots:
+            if ma.material:
+                if ma.material.node_tree:
+                    for no in ma.material.node_tree.nodes:
+                        if no.type in {'EMISSION', 'GROUP'}:
+                            for ou in no.outputs:
+                                if ou.links:
+                                    if no.type == 'GROUP':
+                                        for gno in no.node_tree.nodes:
+                                            if gno.type == 'EMISSION':
+                                                for gou in gno.outputs:
+                                                    if ou.links and gou.links:
+                                                        is_emission = True
+
+                                    elif no.type == 'EMISSION':
+                                        if ou.links:
+                                            is_emission = True
+    return is_emission
+
 # FEATURE: Refresh Scene!
 class AMTH_SCENE_OT_refresh(Operator):
     """Refresh the current scene"""
 # FEATURE: Refresh Scene!
 class AMTH_SCENE_OT_refresh(Operator):
     """Refresh the current scene"""
@@ -390,7 +420,7 @@ class AMTH_FILE_PT_libraries(Panel):
         libslist = []
 
         # Build the list of folders from libraries
         libslist = []
 
         # Build the list of folders from libraries
-        import os
+        import os.path
 
         for lib in libs:
             directory_name = os.path.dirname(lib.filepath)
 
         for lib in libs:
             directory_name = os.path.dirname(lib.filepath)
@@ -783,20 +813,13 @@ def stats_scene(self, context):
         cameras_selected = 0
         meshlights = 0
         meshlights_visible = 0
         cameras_selected = 0
         meshlights = 0
         meshlights_visible = 0
-    
+
         for ob in context.scene.objects:
         for ob in context.scene.objects:
-            if ob.material_slots:
-                for ma in ob.material_slots:
-                    if ma.material:
-                        if ma.material.node_tree:
-                            for no in ma.material.node_tree.nodes:
-                                if no.type == 'EMISSION':
-                                    for ou in no.outputs:
-                                        if ou.links:
-                                            meshlights += 1
-                                            if ob in context.visible_objects:
-                                                meshlights_visible += 1
-                                            break
+            if cycles_is_emission(context, ob):
+                meshlights += 1
+                if ob in context.visible_objects:
+                    meshlights_visible += 1
+
             if ob in context.selected_objects:
                 if ob.type == 'CAMERA':
                     cameras_selected += 1
             if ob in context.selected_objects:
                 if ob.type == 'CAMERA':
                     cameras_selected += 1
@@ -914,14 +937,9 @@ class AMTH_OBJECT_OT_select_meshlights(Operator):
         bpy.ops.object.select_all(action='DESELECT')
 
         for ob in context.scene.objects:
         bpy.ops.object.select_all(action='DESELECT')
 
         for ob in context.scene.objects:
-            if ob.material_slots:
-                for ma in ob.material_slots:
-                    if ma.material:
-                        if ma.material.node_tree:
-                            for no in ma.material.node_tree.nodes:
-                                if no.type == 'EMISSION':
-                                    ob.select = True
-                                    context.scene.objects.active = ob
+            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")
 
         if not context.selected_objects and not context.scene.objects.active:
             self.report({'INFO'}, "No meshlights to select")
@@ -1102,7 +1120,6 @@ def render_cycles_scene_samples(self, context):
     if (len(scene.render.layers) > 1) or \
         (len(bpy.data.scenes) > 1):
 
     if (len(scene.render.layers) > 1) or \
         (len(bpy.data.scenes) > 1):
 
-        layout.separator()
         box = layout.box()
         row = box.row(align=True)
         col = row.column(align=True)
         box = layout.box()
         row = box.row(align=True)
         col = row.column(align=True)
@@ -1395,19 +1412,19 @@ class AMTH_SCENE_OT_cycles_shader_list_nodes_clear(Operator):
         print("* Cleared Cycles Materials List")
         return {'FINISHED'}
 
         print("* Cleared Cycles Materials List")
         return {'FINISHED'}
 
-class AMTH_SCENE_OT_amaranth_debug_lamp_select(Operator):
-    '''Select Lamp'''
-    bl_idname = "scene.amaranth_debug_lamp_select"
-    bl_label = "Select Lamp"
-    lamp = bpy.props.StringProperty()
+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):
  
     def execute(self, context):
-        if self.lamp:
-            lamp = bpy.data.objects[self.lamp]
+        if self.object:
+            object = bpy.data.objects[self.object]
 
             bpy.ops.object.select_all(action='DESELECT')
 
             bpy.ops.object.select_all(action='DESELECT')
-            lamp.select = True
-            context.scene.objects.active = lamp
+            object.select = True
+            context.scene.objects.active = object
 
         return{'FINISHED'}
 
 
         return{'FINISHED'}
 
@@ -1445,10 +1462,11 @@ class AMTH_SCENE_OT_list_missing_node_links(Operator):
                                         "[F] " if ob.use_fake_user else "",
                                         ob.name))
 
                                         "[F] " if ob.use_fake_user else "",
                                         ob.name))
 
-                            missing_groups.append("NG: %s%s%s [%s]%s%s\n" % (
+                            missing_groups.append("MA: %s%s%s [%s]%s%s%s\n" % (
                                 "[L] " if ma.library else "",
                                 "[F] " if ma.use_fake_user else "",
                                 ma.name, ma.users,
                                 "[L] " if ma.library else "",
                                 "[F] " if ma.use_fake_user else "",
                                 ma.name, ma.users,
+                                " *** No users *** " if ma.users == 0 else "",
                                 "\nLI: %s" % 
                                 ma.library.filepath if ma.library else "",
                                 "\nOB: %s" % ',  '.join(users_ngroup) if users_ngroup else ""))
                                 "\nLI: %s" % 
                                 ma.library.filepath if ma.library else "",
                                 "\nOB: %s" % ',  '.join(users_ngroup) if users_ngroup else ""))
@@ -1481,12 +1499,13 @@ class AMTH_SCENE_OT_list_missing_node_links(Operator):
                             if outputs_empty:
                                 self.__class__.count_image_node_unlinked += 1
 
                             if outputs_empty:
                                 self.__class__.count_image_node_unlinked += 1
 
-                                image_nodes_unlinked.append("%s%s%s%s%s [%s]%s%s%s%s\n" % (
+                                image_nodes_unlinked.append("%s%s%s%s%s [%s]%s%s%s%s%s\n" % (
                                     "NO: %s" % no.name,
                                     "\nMA: ",
                                     "[L] " if ma.library else "",
                                     "[F] " if ma.use_fake_user else "",
                                     ma.name, ma.users,
                                     "NO: %s" % no.name,
                                     "\nMA: ",
                                     "[L] " if ma.library else "",
                                     "[F] " if ma.use_fake_user else "",
                                     ma.name, ma.users,
+                                    " *** No users *** " if ma.users == 0 else "",
                                     "\nLI: %s" % 
                                     ma.library.filepath if ma.library else "",
                                     "\nIM: %s" % no.image.name if no.image else "",
                                     "\nLI: %s" % 
                                     ma.library.filepath if ma.library else "",
                                     "\nIM: %s" % no.image.name if no.image else "",
@@ -1497,10 +1516,11 @@ class AMTH_SCENE_OT_list_missing_node_links(Operator):
                             if not no.image or not image_path_exists:
                                 self.__class__.count_images += 1
 
                             if not no.image or not image_path_exists:
                                 self.__class__.count_images += 1
 
-                                missing_images.append("MA: %s%s%s [%s]%s%s%s%s\n" % (
+                                missing_images.append("MA: %s%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,
                                     "[L] " if ma.library else "",
                                     "[F] " if ma.use_fake_user else "",
                                     ma.name, ma.users,
+                                    " *** No users *** " if ma.users == 0 else "",
                                     "\nLI: %s" % 
                                     ma.library.filepath if ma.library else "",
                                     "\nIM: %s" % no.image.name if no.image else "",
                                     "\nLI: %s" % 
                                     ma.library.filepath if ma.library else "",
                                     "\nIM: %s" % no.image.name if no.image else "",
@@ -1526,20 +1546,20 @@ class AMTH_SCENE_OT_list_missing_node_links(Operator):
 
         # List Missing Node Groups
         if missing_groups:
 
         # List Missing Node Groups
         if missing_groups:
-            print("\n* Missing Node Group Links [NG]\n")
+            print("\n* Missing Node Group Links\n")
             for mig in missing_groups:
                 print(mig)
 
         # List Missing Image Nodes
         if missing_images:
             for mig in missing_groups:
                 print(mig)
 
         # List Missing Image Nodes
         if missing_images:
-            print("\n* Missing Image Nodes Link [IM]\n")
+            print("\n* Missing Image Nodes Link\n")
 
             for mii in missing_images:
                 print(mii)
 
         # List Image Nodes with its outputs unlinked
         if image_nodes_unlinked:
 
             for mii in missing_images:
                 print(mii)
 
         # List Image Nodes with its outputs unlinked
         if image_nodes_unlinked:
-            print("\n* Image Nodes Unlinked [IM]\n")
+            print("\n* Image Nodes Unlinked\n")
 
             for nou in image_nodes_unlinked:
                 print(nou)
 
             for nou in image_nodes_unlinked:
                 print(nou)
@@ -1631,10 +1651,11 @@ class AMTH_SCENE_OT_blender_instance_open(Operator):
     bl_idname = "scene.blender_instance_open"
     bl_label = "Open Blender Instance"
     filepath = bpy.props.StringProperty()
     bl_idname = "scene.blender_instance_open"
     bl_label = "Open Blender Instance"
     filepath = bpy.props.StringProperty()
+
     def execute(self, context):
         if self.filepath:
     def execute(self, context):
         if self.filepath:
-            filepath = bpy.path.abspath(self.filepath)
+            import os.path
+            filepath = os.path.normpath(bpy.path.abspath(self.filepath))
 
             import subprocess
             try:
 
             import subprocess
             try:
@@ -1719,13 +1740,13 @@ class AMTH_SCENE_PT_scene_debug(Panel):
                         col = split.column()
                         row = col.row()
                         row.alignment = 'LEFT'
                         col = split.column()
                         row = col.row()
                         row.alignment = 'LEFT'
-                        row.operator("scene.amaranth_debug_lamp_select",
+                        row.operator(AMTH_SCENE_OT_amaranth_object_select.bl_idname,
                                     text='%s %s%s' % (
                                         " [L] " if ob.library else "",
                                         ob.name,
                                         "" if ob.name in context.scene.objects else " [Not in Scene]"),
                                     icon="LAMP_%s" % ob.data.type,
                                     text='%s %s%s' % (
                                         " [L] " if ob.library else "",
                                         ob.name,
                                         "" if ob.name in context.scene.objects else " [Not in Scene]"),
                                     icon="LAMP_%s" % ob.data.type,
-                                    emboss=False).lamp = ob.name
+                                    emboss=False).object = ob.name
                         if ob.library:
                             row = col.row(align=True)
                             row.alignment = "LEFT"
                         if ob.library:
                             row = col.row(align=True)
                             row.alignment = "LEFT"
@@ -1951,9 +1972,14 @@ class AMTH_SCENE_PT_scene_debug(Panel):
 
                 for obs in missing_material_slots_obs:
                     count += 1
 
                 for obs in missing_material_slots_obs:
                     count += 1
-                    col.label(text='%s' % (
-                        missing_material_slots_obs[count-1]),
-                        icon="OBJECT_DATA")
+
+                    row = col.row()
+                    row.alignment = 'LEFT'
+                    row.operator(AMTH_SCENE_OT_amaranth_object_select.bl_idname,
+                                text='%s' % (
+                                    missing_material_slots_obs[count-1]),
+                                icon="OBJECT_DATA",
+                                emboss=False).object = missing_material_slots_obs[count-1]
 
                 if missing_material_slots_lib:
                     col.separator()
 
                 if missing_material_slots_lib:
                     col.separator()
@@ -2316,13 +2342,43 @@ class AMTH_RENDER_OT_cycles_samples_percentage(Operator):
 
         return{'FINISHED'}
 
 
         return{'FINISHED'}
 
+# //FEATURE: Cycles Samples Percentage
+# FEATURE: Jump forward/backward every N frames
+class AMTH_SCREEN_OT_frame_jump(Operator):
+    '''Jump a number of frames forward/backwards'''
+    bl_idname = "screen.amaranth_frame_jump"
+    bl_label = "Jump Frames"
+
+    forward = BoolProperty(default=True)
+
+    def execute(self, context):
+        scene = context.scene
+        preferences = context.user_preferences.addons[__name__].preferences
+
+        if self.forward:
+            scene.frame_current = scene.frame_current + preferences.frames_jump
+        else:
+            scene.frame_current = scene.frame_current - preferences.frames_jump
+
+        return{'FINISHED'}
+
+def ui_userpreferences_edit(self, context):
+    preferences = context.user_preferences.addons[__name__].preferences
+
+    col = self.layout.column()
+    split = col.split(percentage=0.21)
+    split.prop(preferences, "frames_jump",
+               text="Frames to Jump")
+
+# // FEATURE: Jump forward/backward every N frames
+
 classes = (AMTH_SCENE_MT_color_management_presets,
            AMTH_AddPresetColorManagement,
            AMTH_SCENE_PT_scene_debug,
            AMTH_SCENE_OT_refresh,
            AMTH_SCENE_OT_cycles_shader_list_nodes,
            AMTH_SCENE_OT_cycles_shader_list_nodes_clear,
 classes = (AMTH_SCENE_MT_color_management_presets,
            AMTH_AddPresetColorManagement,
            AMTH_SCENE_PT_scene_debug,
            AMTH_SCENE_OT_refresh,
            AMTH_SCENE_OT_cycles_shader_list_nodes,
            AMTH_SCENE_OT_cycles_shader_list_nodes_clear,
-           AMTH_SCENE_OT_amaranth_debug_lamp_select,
+           AMTH_SCENE_OT_amaranth_object_select,
            AMTH_SCENE_OT_list_missing_node_links,
            AMTH_SCENE_OT_list_missing_material_slots,
            AMTH_SCENE_OT_list_missing_material_slots_clear,
            AMTH_SCENE_OT_list_missing_node_links,
            AMTH_SCENE_OT_list_missing_material_slots,
            AMTH_SCENE_OT_list_missing_material_slots_clear,
@@ -2348,7 +2404,8 @@ classes = (AMTH_SCENE_MT_color_management_presets,
            AMTH_POSE_OT_paths_frame_match,
            AMTH_RENDER_OT_cycles_samples_percentage,
            AMTH_RENDER_OT_cycles_samples_percentage_set,
            AMTH_POSE_OT_paths_frame_match,
            AMTH_RENDER_OT_cycles_samples_percentage,
            AMTH_RENDER_OT_cycles_samples_percentage_set,
-           AMTH_FILE_PT_libraries)
+           AMTH_FILE_PT_libraries,
+           AMTH_SCREEN_OT_frame_jump)
 
 addon_keymaps = []
 
 
 addon_keymaps = []
 
@@ -2401,6 +2458,8 @@ def register():
 
     bpy.types.MATERIAL_MT_specials.append(ui_material_remove_unassigned)
 
 
     bpy.types.MATERIAL_MT_specials.append(ui_material_remove_unassigned)
 
+    bpy.types.USERPREF_PT_edit.append(ui_userpreferences_edit)
+
     bpy.app.handlers.render_pre.append(unsimplify_render_pre)
     bpy.app.handlers.render_post.append(unsimplify_render_post)
 
     bpy.app.handlers.render_pre.append(unsimplify_render_pre)
     bpy.app.handlers.render_post.append(unsimplify_render_post)
 
@@ -2419,6 +2478,12 @@ def register():
         kmi = km.keymap_items.new('scene.refresh', 'F5', 'PRESS', shift=False, ctrl=False)
         kmi = km.keymap_items.new('wm.save_reload', 'W', 'PRESS', shift=True, ctrl=True)
 
         kmi = km.keymap_items.new('scene.refresh', 'F5', 'PRESS', shift=False, ctrl=False)
         kmi = km.keymap_items.new('wm.save_reload', 'W', 'PRESS', shift=True, ctrl=True)
 
+        km = kc.keymaps.new(name='Frames')
+        kmi = km.keymap_items.new('screen.amaranth_frame_jump', 'UP_ARROW', 'PRESS', shift=True)
+        kmi.properties.forward = True
+        kmi = km.keymap_items.new('screen.amaranth_frame_jump', 'DOWN_ARROW', 'PRESS', shift=True)
+        kmi.properties.forward = False
+
         km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
         kmi = km.keymap_items.new('view3d.show_only_render', 'Z', 'PRESS', shift=True, alt=True)
 
         km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
         kmi = km.keymap_items.new('view3d.show_only_render', 'Z', 'PRESS', shift=True, alt=True)
 
@@ -2487,6 +2552,8 @@ def unregister():
 
     bpy.types.MATERIAL_MT_specials.remove(ui_material_remove_unassigned)
 
 
     bpy.types.MATERIAL_MT_specials.remove(ui_material_remove_unassigned)
 
+    bpy.types.USERPREF_PT_edit.remove(ui_userpreferences_edit)
+
     bpy.app.handlers.render_pre.remove(unsimplify_render_pre)
     bpy.app.handlers.render_post.remove(unsimplify_render_post)
 
     bpy.app.handlers.render_pre.remove(unsimplify_render_pre)
     bpy.app.handlers.render_post.remove(unsimplify_render_post)