Skip to content
Snippets Groups Projects
scene_amaranth_toolset.py 91.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_AddPresetColorManagement(AddPresetBase, Operator):
    
        """Add or remove a Color Management preset"""
        bl_idname = "scene.color_management_preset_add"
        bl_label = "Add Color Management Preset"
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        preset_menu = "AMTH_SCENE_MT_color_management_presets"
    
    
        preset_defines = [
            "scene = bpy.context.scene"
        ]
    
        preset_values = [
            "scene.view_settings.view_transform",
            "scene.display_settings.display_device",
            "scene.view_settings.exposure",
            "scene.view_settings.gamma",
            "scene.view_settings.look",
            "scene.view_settings.use_curve_mapping",
            "scene.sequencer_colorspace_settings.name",
        ]
    
        preset_subdir = "color"
    
    def ui_color_management_presets(self, context):
        
        layout = self.layout
    
        row = layout.row(align=True)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        row.menu("AMTH_SCENE_MT_color_management_presets", text=bpy.types.AMTH_SCENE_MT_color_management_presets.bl_label)
    
        row.operator("scene.color_management_preset_add", text="", icon="ZOOMIN")
        row.operator("scene.color_management_preset_add", text="", icon="ZOOMOUT").remove_active = True
        layout.separator()
    # // FEATURE: Color Management Presets
    
    
    # FEATURE: Sequencer Extra Info
    def act_strip(context):
        try:
            return context.scene.sequence_editor.active_strip
        except AttributeError:
            return None
    
    def ui_sequencer_extra_info(self, context):
    
        layout = self.layout
        strip = act_strip(context)
    
        if strip:
            seq_type = strip.type
    
            if seq_type and seq_type == 'IMAGE':
                elem = strip.strip_elem_from_frame(context.scene.frame_current)
                if elem:
                    layout.label(text="%s %s" % (
                        elem.filename,
                        "[%s]" % (context.scene.frame_current - strip.frame_start)))
    # // FEATURE: Sequencer Extra Info
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # FEATURE: Normal Node Values, by Lukas Tönne
    def normal_vector_get(self):
        return self.outputs['Normal'].default_value
    
    def normal_vector_set(self, values):
        # default_value allows un-normalized values,
        # do this here to prevent awkward results
        values = Vector(values).normalized()
        self.outputs['Normal'].default_value = values
    
    prop_normal_vector = bpy.props.FloatVectorProperty(
                            name="Normal", size=3, subtype='XYZ',
                            min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0,
                            get=normal_vector_get, set=normal_vector_set
                            )
    
    def act_node(context):
        try:
            return context.active_node
        except AttributeError:
            return None
    
    def ui_node_normal_values(self, context):
    
        node = act_node(context)
    
        if act_node:
            if node and node.type == 'NORMAL':
                self.layout.prop(node, "normal_vector", text="")
    
    # // FEATURE: Normal Node Values, by Lukas Tönne
    
    # FEATURE: Object ID for objects inside DupliGroups
    class AMTH_OBJECT_OT_id_dupligroup(Operator):
        '''Set the Object ID for objects in the dupli group'''
        bl_idname = "object.amaranth_object_id_duplis"
        bl_label = "Apply Object ID to Duplis"
    
        clear = False
    
        @classmethod
        def poll(cls, context):
            return context.active_object.dupli_group
    
        def execute(self, context):
            self.__class__.clear = False
            ob = context.active_object
            amth_text_exists = amaranth_text_startup(context)
    
            script_exists = False
            script_intro = "# OB ID: %s" % ob.name
            obdata = "bpy.data.objects['%s']" % ob.name
            script = "%s" % (
                "\nif %(obdata)s and %(obdata)s.dupli_group and %(obdata)s.pass_index != 0: %(obname)s \n"
                "    for dob in %(obdata)s.dupli_group.objects: %(obname)s \n"
                "        dob.pass_index = %(obdata)s.pass_index %(obname)s \n" %
                    {'obdata' : obdata, 'obname' : script_intro})
    
            for txt in bpy.data.texts:
                if txt.name == amth_text.name:
                    for li in txt.lines:
                        if script_intro == li.body:
                            script_exists = True
                            continue
    
            if not script_exists:
                amth_text.write("\n")
                amth_text.write(script_intro)
                amth_text.write(script)
    
            if ob and ob.dupli_group:
                if ob.pass_index != 0:
                    for dob in ob.dupli_group.objects:
                        dob.pass_index = ob.pass_index
    
            self.report({'INFO'},
                "%s ID: %s to all objects in this Dupli Group" % (
                    "Applied" if not script_exists else "Updated",
                    ob.pass_index))
    
            return{'FINISHED'}
    
    class AMTH_OBJECT_OT_id_dupligroup_clear(Operator):
        '''Clear the Object ID from objects in dupli group'''
        bl_idname = "object.amaranth_object_id_duplis_clear"
        bl_label = "Clear Object ID from Duplis"
    
        @classmethod
        def poll(cls, context):
            return context.active_object.dupli_group
    
        def execute(self, context):
            context.active_object.pass_index = 0
            AMTH_OBJECT_OT_id_dupligroup.clear = True
            amth_text_exists = amaranth_text_startup(context)
            match_first = "# OB ID: %s" % context.active_object.name
    
            if amth_text_exists:
                for txt in bpy.data.texts:
                    if txt.name == amth_text.name:
                        for li in txt.lines:
                            if match_first in li.body:
                                li.body = ''
                                continue
    
            self.report({'INFO'}, "Object IDs back to normal")
            return{'FINISHED'}
    
    def ui_object_id_duplis(self, context):
    
        if context.active_object.dupli_group:
            split = self.layout.split()
            row = split.row(align=True)
            row.enabled = context.active_object.pass_index != 0
            row.operator(
                AMTH_OBJECT_OT_id_dupligroup.bl_idname)
            row.operator(
                AMTH_OBJECT_OT_id_dupligroup_clear.bl_idname,
                icon="X", text="")
            split.separator()
    
            if AMTH_OBJECT_OT_id_dupligroup.clear:
                self.layout.label(text="Next time you save/reload this file, "
                                            "object IDs will be back to normal",
                                  icon="INFO")
    
    # // FEATURE: Object ID for objects inside DupliGroups
    # UI: Warning about Z not connected when using EXR
    def ui_render_output_z(self, context):
    
        scene = bpy.context.scene
        image = scene.render.image_settings
        if scene.render.use_compositing and \
            image.file_format == 'OPEN_EXR' and \
            image.use_zbuffer:
            if scene.node_tree and scene.node_tree.nodes:
                for no in scene.node_tree.nodes:
                    if no.type == 'COMPOSITE':
                        if not no.inputs['Z'].is_linked:
                            self.layout.label(
                                text="The Z output in node \"%s\" is not connected" % 
                                    no.name, icon="ERROR")
    
    # // UI: Warning about Z not connected
    
    # FEATURE: Delete Materials not assigned to any verts
    class AMTH_OBJECT_OT_material_remove_unassigned(Operator):
        '''Remove materials not assigned to any vertex'''
        bl_idname = "object.amaranth_object_material_remove_unassigned"
        bl_label = "Remove Unassigned Materials"
    
        @classmethod
        def poll(cls, context):
            return context.active_object.material_slots
    
        def execute(self, context):
    
            act_ob = context.active_object
            count = len(act_ob.material_slots)
            materials_removed = []
            act_ob.active_material_index = 0
    
            for slot in act_ob.material_slots:
                count -= 1
    
                bpy.ops.object.mode_set(mode='EDIT')
                bpy.ops.mesh.select_all(action='DESELECT')
                act_ob.active_material_index = count
                bpy.ops.object.material_slot_select()
                
                if act_ob.data.total_vert_sel == 0 or \
                    (len(act_ob.material_slots) == 1 and not \
                        act_ob.material_slots[0].material):
                    materials_removed.append(
                        "%s" % act_ob.active_material.name if act_ob.active_material else "Empty")
                    bpy.ops.object.mode_set(mode='OBJECT')
                    bpy.ops.object.material_slot_remove()
                else:
                    pass
    
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.mesh.select_all(action='DESELECT')
            bpy.ops.object.mode_set(mode='OBJECT')
    
            if materials_removed:
                print("\n* Removed %s Unassigned Materials \n" % len(materials_removed))
    
                count_mr = 0
    
                for mr in materials_removed:
                    count_mr += 1
                    print("%0.2d. %s" % (count_mr, materials_removed[count_mr - 1]))
    
                print("\n")
                self.report({'INFO'}, "Removed %s Unassigned Materials" %
                    len(materials_removed))
    
            return{'FINISHED'}
    
    def ui_material_remove_unassigned(self, context):
    
        self.layout.operator(
            AMTH_OBJECT_OT_material_remove_unassigned.bl_idname,
            icon="X")
    
    # // FEATURE: Delete Materials not assigned to any verts
    
    # FEATURE: Cycles Samples Percentage
    class AMTH_RENDER_OT_cycles_samples_percentage_set(Operator):
        '''Save the current number of samples per shader as final (gets saved in .blend)'''
        bl_idname = "scene.amaranth_cycles_samples_percentage_set"
        bl_label = "Set as Render Samples"
    
        def execute(self, context):
            cycles = context.scene.cycles
            cycles.use_samples_final = True
    
            context.scene['amth_cycles_samples_final'] = [
                cycles.diffuse_samples,
                cycles.glossy_samples,
                cycles.transmission_samples,
                cycles.ao_samples,
                cycles.mesh_light_samples,
                cycles.subsurface_samples,
                cycles.volume_samples]
    
            self.report({'INFO'}, "Render Samples Saved")
    
            return{'FINISHED'}
    
    
    class AMTH_RENDER_OT_cycles_samples_percentage(Operator):
        '''Set a percentage of the final render samples'''
        bl_idname = "scene.amaranth_cycles_samples_percentage"
        bl_label = "Set Render Samples Percentage"
    
        percent = IntProperty(
                    name="Percentage",
                    description="Percentage to divide render samples by",
                    subtype='PERCENTAGE',
                    default=0)
    
        def execute(self, context):
            percent = self.percent
            cycles = context.scene.cycles
            cycles_samples_final = context.scene['amth_cycles_samples_final']
    
            cycles.use_samples_final = False
    
            if percent == 100:
                cycles.use_samples_final = True
    
            cycles.diffuse_samples = int((cycles_samples_final[0] / 100) * percent)
            cycles.glossy_samples = int((cycles_samples_final[1] / 100) * percent)
            cycles.transmission_samples = int((cycles_samples_final[2] / 100) * percent)
            cycles.ao_samples = int((cycles_samples_final[3] / 100) * percent)
            cycles.mesh_light_samples = int((cycles_samples_final[4] / 100) * percent)
            cycles.subsurface_samples = int((cycles_samples_final[5] / 100) * percent)
            cycles.volume_samples = int((cycles_samples_final[6] / 100) * percent)
    
            return{'FINISHED'}
    
    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_list_missing_node_links,
               AMTH_SCENE_OT_list_missing_material_slots,
               AMTH_SCENE_OT_list_missing_material_slots_clear,
               AMTH_SCENE_OT_blender_instance_open,
               AMTH_WM_OT_save_reload,
               AMTH_MESH_OT_find_asymmetric,
               AMTH_MESH_OT_make_symmetric,
               AMTH_NODE_OT_AddTemplateVignette,
               AMTH_NODE_MT_amaranth_templates,
               AMTH_FILE_OT_directory_current_blend,
               AMTH_FILE_OT_directory_go_to,
               AMTH_NODE_PT_indices,
               AMTH_NODE_PT_simplify,
               AMTH_NODE_OT_toggle_mute,
               AMTH_NODE_OT_show_active_node_image,
               AMTH_VIEW3D_OT_render_border_camera,
               AMTH_VIEW3D_OT_show_only_render,
               AMTH_OBJECT_OT_select_meshlights,
               AMTH_OBJECT_OT_id_dupligroup,
               AMTH_OBJECT_OT_id_dupligroup_clear,
               AMTH_OBJECT_OT_material_remove_unassigned,
               AMTH_POSE_OT_paths_clear_all,
               AMTH_POSE_OT_paths_frame_match,
               AMTH_RENDER_OT_cycles_samples_percentage,
               AMTH_RENDER_OT_cycles_samples_percentage_set,
               AMTH_FILE_PT_libraries)
    
    
    addon_keymaps = []
    
    def register():
    
        bpy.utils.register_class(AmaranthToolsetPreferences)
    
        # UI: Register the panel
        init_properties()
        for c in classes:
            bpy.utils.register_class(c)
    
        bpy.types.VIEW3D_MT_object_specials.append(button_refresh)
        bpy.types.VIEW3D_MT_object_specials.append(button_render_border_camera)
        bpy.types.VIEW3D_MT_object_specials.append(button_camera_passepartout)
    
        bpy.types.INFO_MT_file.append(button_save_reload)
        bpy.types.INFO_HT_header.append(stats_scene)
    
        bpy.types.VIEW3D_MT_object_specials.append(button_frame_current) # Current Frame
        bpy.types.VIEW3D_MT_pose_specials.append(button_frame_current)
        bpy.types.VIEW3D_MT_select_object.append(button_select_meshlights)
    
        bpy.types.TIME_HT_header.append(label_timeline_extra_info) # Timeline Extra Info
    
        bpy.types.NODE_HT_header.append(node_templates_pulldown)
        bpy.types.NODE_HT_header.append(node_stats)
    
        bpy.types.NODE_HT_header.append(node_shader_extra)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.NODE_PT_active_node_properties.append(ui_node_normal_values)
    
        bpy.types.CyclesRender_PT_sampling.append(render_cycles_scene_samples)
    
    
        bpy.types.FILEBROWSER_HT_header.append(button_directory_current_blend)
    
        bpy.types.SCENE_PT_simplify.append(unsimplify_ui)
    
        bpy.types.CyclesScene_PT_simplify.append(unsimplify_ui)
    
        bpy.types.DATA_PT_display.append(pose_motion_paths_ui)
    
        bpy.types.RENDER_PT_dimensions.append(render_final_resolution_ui)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.RENDER_PT_output.append(ui_render_output_z)
    
    
        bpy.types.SCENE_PT_color_management.prepend(ui_color_management_presets)
    
        bpy.types.SEQUENCER_HT_header.append(ui_sequencer_extra_info)
    
        bpy.types.OBJECT_PT_duplication.append(ui_dupli_group_library_path)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.OBJECT_PT_relations.append(ui_object_id_duplis)
    
        bpy.types.MATERIAL_MT_specials.append(ui_material_remove_unassigned)
    
    
        bpy.app.handlers.render_pre.append(unsimplify_render_pre)
        bpy.app.handlers.render_post.append(unsimplify_render_post)
    
        wm = bpy.context.window_manager
        kc = wm.keyconfigs.addon
        if kc:
    
            km = kc.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
            km.keymap_items.new("node.show_active_node_image", 'ACTIONMOUSE', 'RELEASE')
            km.keymap_items.new("node.show_active_node_image", 'SELECTMOUSE', 'RELEASE')
    
            km = kc.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
            kmi = km.keymap_items.new('wm.call_menu', 'W', 'PRESS')
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            kmi.properties.name = "AMTH_NODE_MT_amaranth_templates"
    
            km = kc.keymaps.new(name='Window')
            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='3D View', space_type='VIEW_3D')
            kmi = km.keymap_items.new('view3d.show_only_render', 'Z', 'PRESS', shift=True, alt=True)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            km = kc.keymaps.new(name='Graph Editor', space_type='GRAPH_EDITOR')
            kmi = km.keymap_items.new('wm.context_set_enum', 'TAB', 'PRESS', ctrl=True)
            kmi.properties.data_path = 'area.type'
            kmi.properties.value = 'DOPESHEET_EDITOR'
    
            km = kc.keymaps.new(name='Dopesheet', space_type='DOPESHEET_EDITOR')
            kmi = km.keymap_items.new('wm.context_set_enum', 'TAB', 'PRESS', ctrl=True)
            kmi.properties.data_path = 'area.type'
            kmi.properties.value = 'GRAPH_EDITOR'
    
            km = kc.keymaps.new(name='Dopesheet', space_type='DOPESHEET_EDITOR')
            kmi = km.keymap_items.new('wm.context_toggle_enum', 'TAB', 'PRESS', shift=True)
            kmi.properties.data_path = 'space_data.mode'
            kmi.properties.value_1 = 'ACTION'
            kmi.properties.value_2 = 'DOPESHEET'
    
    
            addon_keymaps.append((km, kmi))
    
    def unregister():
    
        bpy.utils.unregister_class(AmaranthToolsetPreferences)
    
        for c in classes:
            bpy.utils.unregister_class(c)
    
        bpy.types.VIEW3D_MT_object_specials.remove(button_refresh)
        bpy.types.VIEW3D_MT_object_specials.remove(button_render_border_camera)
        bpy.types.VIEW3D_MT_object_specials.remove(button_camera_passepartout)
    
        bpy.types.INFO_MT_file.remove(button_save_reload)
        bpy.types.INFO_HT_header.remove(stats_scene)
    
        bpy.types.VIEW3D_MT_object_specials.remove(button_frame_current)
        bpy.types.VIEW3D_MT_pose_specials.remove(button_frame_current)
        bpy.types.VIEW3D_MT_select_object.remove(button_select_meshlights)
    
        bpy.types.TIME_HT_header.remove(label_timeline_extra_info)
    
        bpy.types.NODE_HT_header.remove(node_templates_pulldown)
        bpy.types.NODE_HT_header.remove(node_stats)
    
        bpy.types.NODE_HT_header.remove(node_shader_extra)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.NODE_PT_active_node_properties.remove(ui_node_normal_values)
    
        bpy.types.CyclesRender_PT_sampling.remove(render_cycles_scene_samples)
    
    
        bpy.types.FILEBROWSER_HT_header.remove(button_directory_current_blend)
    
        bpy.types.SCENE_PT_simplify.remove(unsimplify_ui)
    
        bpy.types.CyclesScene_PT_simplify.remove(unsimplify_ui)
    
        bpy.types.DATA_PT_display.remove(pose_motion_paths_ui)
    
        bpy.types.RENDER_PT_dimensions.remove(render_final_resolution_ui)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.RENDER_PT_output.remove(ui_render_output_z)
    
    
        bpy.types.SCENE_PT_color_management.remove(ui_color_management_presets)
    
        bpy.types.SEQUENCER_HT_header.remove(ui_sequencer_extra_info)
    
        bpy.types.OBJECT_PT_duplication.remove(ui_dupli_group_library_path)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.OBJECT_PT_relations.remove(ui_object_id_duplis)
    
        bpy.types.MATERIAL_MT_specials.remove(ui_material_remove_unassigned)
    
    
        bpy.app.handlers.render_pre.remove(unsimplify_render_pre)
        bpy.app.handlers.render_post.remove(unsimplify_render_post)
    
        for km, kmi in addon_keymaps:
            km.keymap_items.remove(kmi)
        addon_keymaps.clear()
    
        clear_properties()
    
    if __name__ == "__main__":
        register()