Skip to content
Snippets Groups Projects
scene_amaranth_toolset.py 116 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    bl_info = {
        "name": "Amaranth Toolset",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        "version": (0, 9, 4),
    
        "blender": (2, 70),
        "location": "Everywhere!",
    
        "description": "A collection of tools and settings to improve productivity",
        "warning": "",
        "wiki_url": "http://pablovazquez.org/amaranth",
        "tracker_url": "",
        "category": "Scene"}
    
    
    import bpy
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    import bmesh
    
    from bpy.types import Operator, AddonPreferences, Panel, Menu
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    from bpy.props import (BoolProperty, EnumProperty,
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                           FloatProperty, FloatVectorProperty,
                           IntProperty, StringProperty)
    
    from mathutils import Vector
    from bpy.app.handlers import persistent
    
    from bl_operators.presets import AddPresetBase
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # Addon wide, we need to know if cycles is available
    global cycles_exists
    cycles_exists = 'cycles' in dir(bpy.types.Scene)
    
    
    # Preferences
    class AmaranthToolsetPreferences(AddonPreferences):
        bl_idname = __name__
        use_frame_current = BoolProperty(
                name="Current Frame Slider",
                description="Set the current frame from the Specials menu in the 3D View",
                default=True,
                )
        use_file_save_reload = BoolProperty(
                name="Save & Reload File",
                description="File menu > Save & Reload, or Ctrl + Shift + W",
                default=True,
                )
    
        use_scene_refresh = BoolProperty(
                name="Refresh Scene",
                description="Specials Menu [W], or hit F5",
                default=True,
                )
        use_timeline_extra_info = BoolProperty(
                name="Timeline Extra Info",
                description="Timeline Header",
                default=True,
                )
        use_image_node_display = BoolProperty(
                name="Active Image Node in Editor",
                description="Display active node image in image editor",
                default=True,
                )
        use_scene_stats = BoolProperty(
                name="Extra Scene Statistics",
                description="Display extra scene statistics in Info editor's header",
                default=True,
                )
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        frames_jump = IntProperty(
                    name="Frames",
                    description="Number of frames to jump forward/backward",
                    default=10,
                    min=1)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        use_layers_for_render = BoolProperty(
                name="Current Layers for Render",
                description="Save the layers that should be enabled for render",
                default=True,
                )
    
    
    
        def draw(self, context):
            layout = self.layout
    
            layout.label(
                text="Here you can enable or disable specific tools, "
                     "in case they interfere with others or are just plain annoying")
    
            split = layout.split(percentage=0.25)
    
            col = split.column()
            sub = col.column(align=True)
            sub.label(text="3D View", icon="VIEW3D")
            sub.prop(self, "use_frame_current")
            sub.prop(self, "use_scene_refresh")
    
            sub.separator()
    
            sub.label(text="General", icon="SCENE_DATA")
            sub.prop(self, "use_file_save_reload")
            sub.prop(self, "use_timeline_extra_info")
            sub.prop(self, "use_scene_stats")
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            sub.prop(self, "use_layers_for_render")
    
            sub.label(text="Nodes Editor", icon="NODETREE")
    
            sub.prop(self, "use_image_node_display")
    
            col = split.column()
            sub = col.column(align=True)
            sub.label(text="")
            sub.label(
                text="Set the current frame from the Specials menu in the 3D View [W]")
            sub.label(
                text="Refresh the current Scene. Hotkey: F5 or in Specials menu [W]")
    
            sub.separator()
            sub.label(text="") # General
            sub.label(
                text="Quickly save and reload the current file (no warning!). "
                     "File menu or Ctrl+Shift+W")
            sub.label(
                text="SMPTE Timecode and frames left/ahead on Timeline's header")
            sub.label(
                text="Display extra statistics for Scenes, Cameras, and Meshlights (Cycles)")
    
            sub.separator()
            sub.label(text="") # Nodes
            sub.label(
                text="When selecting an Image node, display it on the Image editor "
                     "(if any)")
    
    # Properties
    def init_properties():
    
        scene = bpy.types.Scene
        node = bpy.types.Node
        nodes_compo = bpy.types.CompositorNodeTree
    
    
        scene.use_unsimplify_render = BoolProperty(
    
            default=False,
            name="Unsimplify Render",
            description="Disable Simplify during render")
    
        scene.simplify_status = BoolProperty(default=False)
    
        node.use_matching_indices = BoolProperty(
    
            default=True,
            description="If disabled, display all available indices")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        nodes_compo_types = [
    
            ("ALL", "All Types", "", 0),
            ("BLUR", "Blur", "", 1),
            ("BOKEHBLUR", "Bokeh Blur", "", 2),
            ("VECBLUR", "Vector Blur", "", 3),
            ("DEFOCUS", "Defocus", "", 4),
            ("R_LAYERS", "Render Layer", "", 5)
            ]
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        nodes_compo.types = EnumProperty(
            items=nodes_compo_types, name = "Types")
    
        nodes_compo.toggle_mute = BoolProperty(default=False)
        node.status = BoolProperty(default=False)
    
        # Scene Debug
        # Cycles Node Types
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        if cycles_exists:
            cycles_shader_node_types = [
                ("BSDF_DIFFUSE", "Diffuse BSDF", "", 0),
                ("BSDF_GLOSSY", "Glossy BSDF", "", 1),
                ("BSDF_TRANSPARENT", "Transparent BSDF", "", 2),
                ("BSDF_REFRACTION", "Refraction BSDF", "", 3),
                ("BSDF_GLASS", "Glass BSDF", "", 4),
                ("BSDF_TRANSLUCENT", "Translucent BSDF", "", 5),
                ("BSDF_ANISOTROPIC", "Anisotropic BSDF", "", 6),
                ("BSDF_VELVET", "Velvet BSDF", "", 7),
                ("BSDF_TOON", "Toon BSDF", "", 8),
                ("SUBSURFACE_SCATTERING", "Subsurface Scattering", "", 9),
                ("EMISSION", "Emission", "", 10),
                ("BSDF_HAIR", "Hair BSDF", "", 11),
                ("BACKGROUND", "Background", "", 12),
                ("AMBIENT_OCCLUSION", "Ambient Occlusion", "", 13),
                ("HOLDOUT", "Holdout", "", 14),
                ("VOLUME_ABSORPTION", "Volume Absorption", "", 15),
                ("VOLUME_SCATTER", "Volume Scatter", "", 16)
                ]
    
            scene.amaranth_cycles_node_types = EnumProperty(
                items=cycles_shader_node_types, name = "Shader")
    
            scene.amaranth_cycles_list_sampling = BoolProperty(
                default=False,
                name="Samples Per:")
    
            bpy.types.CyclesRenderSettings.use_samples_final = BoolProperty(
                name="Use Final Render Samples",
                description="Use current shader samples as final render samples",
                default=False)
    
        scene.amaranth_lighterscorner_list_meshlights = BoolProperty(
    
            default=False,
    
            name="List Meshlights",
            description="Include light emitting meshes on the list")
    
    
        scene.amaranth_debug_scene_list_missing_images = BoolProperty(
            default=False,
            name="List Missing Images",
            description="Display a list of all the missing images")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bpy.types.ShaderNodeNormal.normal_vector = prop_normal_vector
        bpy.types.CompositorNodeNormal.normal_vector = prop_normal_vector
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
        bpy.types.Object.is_keyframe = is_keyframe
    
        scene.amth_wire_toggle_scene_all = BoolProperty(
            default=False,
            name="All Scenes",
            description="Toggle wire on objects in all scenes")
        scene.amth_wire_toggle_is_selected = BoolProperty(
            default=False,
            name="Only Selected",
            description="Only toggle wire on selected objects")
        scene.amth_wire_toggle_edges_all = BoolProperty(
            default=True,
            name="All Edges",
            description="Draw all edges")
        scene.amth_wire_toggle_optimal = BoolProperty(
            default=False,
            name="Optimal Display",
            description="Skip drawing/rendering of interior subdivided edges "
                        "on meshes with Subdivision Surface modifier")
    
    def clear_properties():
        props = (
            "use_unsimplify_render",
            "simplify_status",
            "use_matching_indices",
            "use_simplify_nodes_vector",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            "status",
            "types",
            "toggle_mute",
            "amaranth_cycles_node_types",
    
            "amaranth_lighterscorner_list_meshlights",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            "amaranth_debug_scene_list_missing_images",
            "amarath_cycles_list_sampling",
            "normal_vector",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            "use_samples_final",
            'amth_wire_toggle_is_selected',
            'amth_wire_toggle_scene_all',
            "amth_wire_toggle_edges_all",
            "amth_wire_toggle_optimal"
    
        wm = bpy.context.window_manager
        for p in props:
            if p in wm:
                del wm[p]
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # Some settings are bound to be saved on a startup py file
    def amaranth_text_startup(context):
    
        amth_text_name = "AmaranthStartup.py"
        amth_text_exists = False
    
        global amth_text
    
        try:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if bpy.data.texts:
                for tx in bpy.data.texts:
                    if tx.name == amth_text_name:
                        amth_text_exists = True
                        amth_text = bpy.data.texts[amth_text_name]
                        break
                    else:
                        amth_text_exists = False
    
            if not amth_text_exists:
                bpy.ops.text.new()
                amth_text = bpy.data.texts[-1]
                amth_text.name = amth_text_name
                amth_text.write("# Amaranth Startup Script\nimport bpy\n\n")
                amth_text.use_module = True
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    
            return amth_text_exists
        except AttributeError:
            return None
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # FUNCTION: Check if material has Emission (for select and stats)
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    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 and ma.material.node_tree.nodes:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        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' and no.node_tree and no.node_tree.nodes:
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                                            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
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    # FUNCTION: Check if object has keyframes for a specific frame
    def is_keyframe(ob, frame):
        if ob is not None and ob.animation_data is not None and ob.animation_data.action is not None:
            for fcu in ob.animation_data.action.fcurves:
                if frame in (p.co.x for p in fcu.keyframe_points):
                    return True
        return False
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_SCENE_OT_refresh(Operator):
    
        """Refresh the current scene"""
        bl_idname = "scene.refresh"
        bl_label = "Refresh!"
    
        def execute(self, context):
            preferences = context.user_preferences.addons[__name__].preferences
            scene = context.scene
    
    
            if preferences.use_scene_refresh:    
    
                # Changing the frame is usually the best way to go
                scene.frame_current = scene.frame_current
                self.report({"INFO"}, "Scene Refreshed!")
    
            return {'FINISHED'}
    
    def button_refresh(self, context):
    
        preferences = context.user_preferences.addons[__name__].preferences
    
        if preferences.use_scene_refresh:
            self.layout.separator()
            self.layout.operator(
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                AMTH_SCENE_OT_refresh.bl_idname,
    
                text="Refresh!",
                icon='FILE_REFRESH')
    # // FEATURE: Refresh Scene!
    
    # FEATURE: Save & Reload
    def save_reload(self, context, path):
    
        if path:
            bpy.ops.wm.save_mainfile()
            self.report({'INFO'}, "Saved & Reloaded")
            bpy.ops.wm.open_mainfile("EXEC_DEFAULT", filepath=path)
        else:
            bpy.ops.wm.save_as_mainfile("INVOKE_AREA")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_WM_OT_save_reload(Operator):
    
        """Save and Reload the current blend file"""
        bl_idname = "wm.save_reload"
        bl_label = "Save & Reload"
    
        def execute(self, context):
    
            path = bpy.data.filepath
            save_reload(self, context, path)
            return {'FINISHED'}
    
    def button_save_reload(self, context):
    
        preferences = context.user_preferences.addons[__name__].preferences
    
        if preferences.use_file_save_reload:
            self.layout.separator()
            self.layout.operator(
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                AMTH_WM_OT_save_reload.bl_idname,
    
                text="Save & Reload",
                icon='FILE_REFRESH')
    # // FEATURE: Save & Reload
    
    # FEATURE: Current Frame
    def button_frame_current(self, context):
    
        preferences = context.user_preferences.addons[__name__].preferences
        scene = context.scene
    
        if preferences.use_frame_current:
            self.layout.separator()
            self.layout.prop(
                scene, "frame_current",
                text="Set Current Frame")
    # // FEATURE: Current Frame
    
    # FEATURE: Timeline Time + Frames Left
    def label_timeline_extra_info(self, context):
    
        preferences = context.user_preferences.addons[__name__].preferences
        layout = self.layout
        scene = context.scene
    
        if preferences.use_timeline_extra_info:
            row = layout.row(align=True)
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            row.operator(AMTH_SCREEN_OT_keyframe_jump_inbetween.bl_idname, icon="PREV_KEYFRAME", text="").backwards = True
            row.operator(AMTH_SCREEN_OT_keyframe_jump_inbetween.bl_idname, icon="NEXT_KEYFRAME", text="").backwards = False
    
    
            # Check for preview range
            frame_start = scene.frame_preview_start if scene.use_preview_range else scene.frame_start
            frame_end = scene.frame_preview_end if scene.use_preview_range else scene.frame_end
    
            row.label(text="%s / %s" % (bpy.utils.smpte_from_frame(scene.frame_current - frame_start),
                            bpy.utils.smpte_from_frame(frame_end - frame_start)))
    
            if (scene.frame_current > frame_end):
                row.label(text="%s Frames Ahead" % ((frame_end - scene.frame_current) * -1))
            elif (scene.frame_current == frame_start):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                row.label(text="Start Frame (%s left)" % (frame_end - scene.frame_current))
    
            elif (scene.frame_current == frame_end):
                row.label(text="%s End Frame" % scene.frame_current)
            else:
                row.label(text="%s Frames Left" % (frame_end - scene.frame_current))
    
    # // FEATURE: Timeline Time + Frames Left
    
    # FEATURE: Directory Current Blend
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_FILE_OT_directory_current_blend(Operator):
    
        """Go to the directory of the currently open blend file"""
        bl_idname = "file.directory_current_blend"
        bl_label = "Current Blend's Folder"
    
        def execute(self, context):
            bpy.ops.file.select_bookmark(dir='//')
            return {'FINISHED'}
    
    def button_directory_current_blend(self, context):
    
        if bpy.data.filepath:
            self.layout.operator(
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                AMTH_FILE_OT_directory_current_blend.bl_idname,
    
                text="Current Blend's Folder",
                icon='APPEND_BLEND')
    # // FEATURE: Directory Current Blend
    
    
    # FEATURE: Libraries panel on file browser
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_FILE_PT_libraries(Panel):
    
        bl_space_type = 'FILE_BROWSER'
        bl_region_type = 'CHANNELS'
        bl_label = "Libraries"
    
        def draw(self, context):
            layout = self.layout
    
            libs = bpy.data.libraries
            libslist = []
    
            # Build the list of folders from libraries
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            import os.path
    
    
            for lib in libs:
                directory_name = os.path.dirname(lib.filepath)
                libslist.append(directory_name)
    
            # Remove duplicates and sort by name
            libslist = set(libslist)
            libslist = sorted(libslist)
    
            # Draw the box with libs
    
            row = layout.row()
            box = row.box()
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                col = box.column()
    
                for filepath in libslist:
                    if filepath != '//':
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        row = col.row()
    
                        row.alignment = 'LEFT'
                        props = row.operator(
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                            AMTH_FILE_OT_directory_go_to.bl_idname,
    
                            text=filepath, icon="BOOKMARKS",
                            emboss=False)
    
                        props.filepath = filepath
            else:
                box.label(text='No libraries loaded')
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_FILE_OT_directory_go_to(Operator):
    
        """Go to this library's directory"""
        bl_idname = "file.directory_go_to"
        bl_label = "Go To"
    
        filepath = bpy.props.StringProperty(subtype="FILE_PATH")
    
        def execute(self, context):
            bpy.ops.file.select_bookmark(dir=self.filepath)
            return {'FINISHED'}
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_OT_AddTemplateVignette(Operator):
    
        bl_idname = "node.template_add_vignette"
        bl_label = "Add Vignette"
        bl_description = "Add a vignette effect"
        bl_options = {'REGISTER', 'UNDO'}
    
        @classmethod
        def poll(cls, context):
            space = context.space_data
            return space.type == 'NODE_EDITOR' \
                    and space.node_tree is not None \
                    and space.tree_type == 'CompositorNodeTree'
    
        # used as reference the setup scene script from master nazgul
        def _setupNodes(self, context):
            scene = context.scene
            space = context.space_data
            tree = scene.node_tree
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            has_act = True if tree.nodes.active else False
    
    
            bpy.ops.node.select_all(action='DESELECT')
    
            ellipse = tree.nodes.new(type='CompositorNodeEllipseMask')
            ellipse.width = 0.8
            ellipse.height = 0.4
            blur = tree.nodes.new(type='CompositorNodeBlur')
            blur.use_relative = True
            blur.factor_x = 30
            blur.factor_y = 50
            ramp = tree.nodes.new(type='CompositorNodeValToRGB')
            ramp.color_ramp.interpolation = 'B_SPLINE'
            ramp.color_ramp.elements[1].color = (0.6, 0.6, 0.6, 1)
    
            overlay = tree.nodes.new(type='CompositorNodeMixRGB')
            overlay.blend_type = 'OVERLAY'
            overlay.inputs[0].default_value = 0.8
            overlay.inputs[1].default_value = (0.5, 0.5, 0.5, 1)
    
            tree.links.new(ellipse.outputs["Mask"],blur.inputs["Image"])
            tree.links.new(blur.outputs["Image"],ramp.inputs[0])
            tree.links.new(ramp.outputs["Image"],overlay.inputs[2])
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if has_act:
                tree.links.new(tree.nodes.active.outputs[0],overlay.inputs[1])
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            if has_act:
                overlay.location = tree.nodes.active.location
                overlay.location += Vector((350.0, 0.0))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                overlay.location += Vector((space.cursor_location[0], space.cursor_location[1]))
    
            ellipse.location = overlay.location
            ellipse.location += Vector((-715.0, -400))
            ellipse.inputs[0].hide = True
            ellipse.inputs[1].hide = True
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            blur.location = ellipse.location
            blur.location += Vector((300.0, 0.0))
            blur.inputs['Size'].hide = True
    
    
            ramp.location = blur.location
            ramp.location += Vector((175.0, 0))
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            ramp.outputs['Alpha'].hide = True
    
    
            for node in {ellipse, blur, ramp, overlay}:
                node.select = True
                node.show_preview = False
    
            bpy.ops.node.join()
    
            frame = ellipse.parent
            frame.label = 'Vignette'
            frame.use_custom_color = True
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            frame.color = (0.1, 0.1, 0.1)
    
            overlay.parent = None
            overlay.label = 'Vignette Overlay'
    
        def execute(self, context):
            self._setupNodes(context)
    
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_OT_AddTemplateVectorBlur(Operator):
        bl_idname = "node.template_add_vectorblur"
        bl_label = "Add Vector Blur"
        bl_description = "Add a vector blur filter"
        bl_options = {'REGISTER', 'UNDO'}
    
        @classmethod
        def poll(cls, context):
            space = context.space_data
            tree = context.scene.node_tree
            return space.type == 'NODE_EDITOR' \
                    and space.node_tree is not None \
                    and space.tree_type == 'CompositorNodeTree' \
                    and tree \
                    and tree.nodes.active \
                    and tree.nodes.active.type == 'R_LAYERS'
    
        def _setupNodes(self, context):
            scene = context.scene
            space = context.space_data
            tree = scene.node_tree
    
            bpy.ops.node.select_all(action='DESELECT')
    
            act_node = tree.nodes.active
            rlayer = act_node.scene.render.layers[act_node.layer]
    
            if not rlayer.use_pass_vector:
                rlayer.use_pass_vector = True
    
            vblur = tree.nodes.new(type='CompositorNodeVecBlur')
            vblur.use_curved = True
            vblur.factor = 0.5
    
            tree.links.new(act_node.outputs["Image"],vblur.inputs["Image"])
            tree.links.new(act_node.outputs["Z"],vblur.inputs["Z"])
            tree.links.new(act_node.outputs["Speed"],vblur.inputs["Speed"])
    
            if tree.nodes.active:
                vblur.location = tree.nodes.active.location
                vblur.location += Vector((250.0, 0.0))
            else:
                vblur.location += Vector((space.cursor_location[0], space.cursor_location[1]))
    
            vblur.select = True
    
        def execute(self, context):
            self._setupNodes(context)
    
            return {'FINISHED'}
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_MT_amaranth_templates(Menu):
        bl_idname = 'AMTH_NODE_MT_amaranth_templates'
    
        bl_space_type = 'NODE_EDITOR'
        bl_label = "Templates"
        bl_description = "List of Amaranth Templates"
    
        def draw(self, context):
            layout = self.layout
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            layout.operator(
                AMTH_NODE_OT_AddTemplateVectorBlur.bl_idname,
                text="Vector Blur",
                icon='FORCE_HARMONIC')
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                AMTH_NODE_OT_AddTemplateVignette.bl_idname,
    
                text="Vignette",
                icon='COLOR')
    
    def node_templates_pulldown(self, context):
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        if context.space_data.tree_type == 'CompositorNodeTree':
            layout = self.layout
            row = layout.row(align=True)
            row.scale_x = 1.3
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            row.menu("AMTH_NODE_MT_amaranth_templates",
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                icon="NODETREE")
    
    # // FEATURE: Node Templates
    
    def node_stats(self,context):
        if context.scene.node_tree:
            tree_type = context.space_data.tree_type
            nodes = context.scene.node_tree.nodes
            nodes_total = len(nodes.keys())
            nodes_selected = 0
            for n in nodes:
                if n.select:
                    nodes_selected = nodes_selected + 1
    
            if tree_type == 'CompositorNodeTree':
                layout = self.layout
                row = layout.row(align=True)
                row.label(text="Nodes: %s/%s" % (nodes_selected, str(nodes_total)))
    
    # FEATURE: Simplify Compo Nodes
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_PT_simplify(Panel):
    
        '''Simplify Compositor Panel'''
        bl_space_type = 'NODE_EDITOR'
        bl_region_type = 'UI'
        bl_label = 'Simplify'
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
        bl_options = {'DEFAULT_CLOSED'}
    
        @classmethod
        def poll(cls, context):
            space = context.space_data
            return space.type == 'NODE_EDITOR' \
                    and space.node_tree is not None \
                    and space.tree_type == 'CompositorNodeTree'
    
    
        def draw(self, context):
            layout = self.layout
            node_tree = context.scene.node_tree
    
            if node_tree is not None:
                layout.prop(node_tree, 'types')
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                layout.operator(AMTH_NODE_OT_toggle_mute.bl_idname,
    
                    text="Turn On" if node_tree.toggle_mute else "Turn Off",
                    icon='RESTRICT_VIEW_OFF' if node_tree.toggle_mute else 'RESTRICT_VIEW_ON')
    
                if node_tree.types == 'VECBLUR':
                    layout.label(text="This will also toggle the Vector pass {}".format(
                                        "on" if node_tree.toggle_mute else "off"), icon="INFO")
    
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_OT_toggle_mute(Operator):
    
        """"""
        bl_idname = "node.toggle_mute"
        bl_label = "Toggle Mute"
    
        def execute(self, context):
            scene = context.scene
            node_tree = scene.node_tree
            node_type = node_tree.types
            rlayers = scene.render
    
            if not 'amaranth_pass_vector' in scene.keys():
                scene['amaranth_pass_vector'] = []
    
            #can't extend() the list, so make a dummy one
            pass_vector = scene['amaranth_pass_vector']
    
            if not pass_vector:
                pass_vector = []
    
            if node_tree.toggle_mute:
                for node in node_tree.nodes:
                    if node_type == 'ALL':
                        node.mute = node.status
                    if node.type == node_type:
                        node.mute = node.status
                    if node_type == 'VECBLUR':
                        for layer in rlayers.layers:
                            if layer.name in pass_vector:
                                layer.use_pass_vector = True
                                pass_vector.remove(layer.name)
    
                    node_tree.toggle_mute = False
    
            else:
                for node in node_tree.nodes:
                    if node_type == 'ALL':
                        node.mute = True
                    if node.type == node_type:
                        node.status = node.mute
                        node.mute = True
                    if node_type == 'VECBLUR':
                        for layer in rlayers.layers:
                            if layer.use_pass_vector:
                                pass_vector.append(layer.name)
                                layer.use_pass_vector = False
                                pass
    
                    node_tree.toggle_mute = True
    
            # Write back to the custom prop
            pass_vector = sorted(set(pass_vector))
            scene['amaranth_pass_vector'] = pass_vector
    
            return {'FINISHED'}
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_NODE_PT_indices(Panel):
    
        '''Object / Material Indices Panel'''
        bl_space_type = 'NODE_EDITOR'
        bl_region_type = 'UI'
        bl_label = 'Object / Material Indices'
        bl_options = {'DEFAULT_CLOSED'}
    
        @classmethod
        def poll(cls, context):
            node = context.active_node
            return node and node.type == 'ID_MASK'
    
        def draw(self, context):
            layout = self.layout
    
            objects = bpy.data.objects
            materials = bpy.data.materials
            node = context.active_node
    
            show_ob_id = False
            show_ma_id = False
            matching_ids = False
    
            if context.active_object:
                ob_act = context.active_object
            else:
                ob_act = False
    
            for ob in objects:
                if ob and ob.pass_index > 0:
                    show_ob_id = True
            for ma in materials:
                if ma and ma.pass_index > 0:
                    show_ma_id = True
    
            row = layout.row(align=True)  
    
            row.prop(node, 'index', text="Mask Index")
            row.prop(node, 'use_matching_indices', text="Only Matching IDs")
    
            layout.separator()
    
            if not show_ob_id and not show_ma_id:
                layout.label(text="No objects or materials indices so far.", icon="INFO")
    
            if show_ob_id:
                split = layout.split()
                col = split.column()
                col.label(text="Object Name")
                split.label(text="ID Number")
                row = layout.row()
                for ob in objects:
                    icon = "OUTLINER_DATA_" + ob.type
                    if ob.library:
                        icon = "LIBRARY_DATA_DIRECT"
                    elif ob.is_library_indirect:
                        icon = "LIBRARY_DATA_INDIRECT"
    
                    if ob and node.use_matching_indices \
                          and ob.pass_index == node.index \
                          and ob.pass_index != 0:
                        matching_ids = True
                        row.label(
                          text="[{}]".format(ob.name)
                              if ob_act and ob.name == ob_act.name else ob.name,
                          icon=icon)
                        row.label(text="%s" % ob.pass_index)
                        row = layout.row()
    
                    elif ob and not node.use_matching_indices \
                            and ob.pass_index > 0:
    
                        matching_ids = True
                        row.label(
                          text="[{}]".format(ob.name)
                              if ob_act and ob.name == ob_act.name else ob.name,
                          icon=icon)
                        row.label(text="%s" % ob.pass_index)
                        row = layout.row()
    
                if node.use_matching_indices and not matching_ids:
                    row.label(text="No objects with ID %s" % node.index, icon="INFO")
    
                layout.separator()
    
            if show_ma_id:
                split = layout.split()
                col = split.column()
                col.label(text="Material Name")
                split.label(text="ID Number")
                row = layout.row()
    
                for ma in materials:
                    icon = "BLANK1"
                    if ma.use_nodes:
                        icon = "NODETREE"
                    elif ma.library:
                        icon = "LIBRARY_DATA_DIRECT"
                        if ma.is_library_indirect:
                            icon = "LIBRARY_DATA_INDIRECT"
    
                    if ma and node.use_matching_indices \
                          and ma.pass_index == node.index \
                          and ma.pass_index != 0:
                        matching_ids = True
                        row.label(text="%s" % ma.name, icon=icon)
                        row.label(text="%s" % ma.pass_index)
                        row = layout.row()
    
                    elif ma and not node.use_matching_indices \
                            and ma.pass_index > 0:
    
                        matching_ids = True
                        row.label(text="%s" % ma.name, icon=icon)
                        row.label(text="%s" % ma.pass_index)
                        row = layout.row()
    
                if node.use_matching_indices and not matching_ids:
                    row.label(text="No materials with ID %s" % node.index, icon="INFO")
    
    
    # // FEATURE: OB/MA ID panel in Node Editor
    
    # FEATURE: Unsimplify on render
    @persistent
    def unsimplify_render_pre(scene):
        render = scene.render
        scene.simplify_status = render.use_simplify
    
        if scene.use_unsimplify_render:
            render.use_simplify = False
    
    @persistent
    def unsimplify_render_post(scene):
        render = scene.render
        render.use_simplify = scene.simplify_status
    
    def unsimplify_ui(self,context):
        scene = bpy.context.scene
        self.layout.prop(scene, 'use_unsimplify_render')
    # //FEATURE: Unsimplify on render
    
    # FEATURE: Extra Info Stats
    def stats_scene(self, context):
    
        preferences = context.user_preferences.addons[__name__].preferences
    
        if preferences.use_scene_stats:
            scenes_count = str(len(bpy.data.scenes))
            cameras_count = str(len(bpy.data.cameras))
            cameras_selected = 0
            meshlights = 0
            meshlights_visible = 0
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                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':
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
                        cameras_selected += 1
    
            meshlights_string = '| Meshlights:{}/{}'.format(meshlights_visible, meshlights)
    
            row = self.layout.row(align=True)
            row.label(text="Scenes:{} | Cameras:{}/{} {}".format(
                       scenes_count, cameras_selected, cameras_count,
                       meshlights_string if context.scene.render.engine == 'CYCLES' else ''))
    
    # //FEATURE: Extra Info Stats
    
    # FEATURE: Camera Bounds as Render Border
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_VIEW3D_OT_render_border_camera(Operator):
    
        """Set camera bounds as render border"""
        bl_idname = "view3d.render_border_camera"
        bl_label = "Camera as Render Border"
    
        @classmethod
        def poll(cls, context):
            return context.space_data.region_3d.view_perspective == 'CAMERA'
    
        def execute(self, context):
            render = context.scene.render
            render.use_border = True
            render.border_min_x = 0
            render.border_min_y = 0
            render.border_max_x = 1
            render.border_max_y = 1
    
            return {'FINISHED'}
    
    def button_render_border_camera(self, context):
    
        view3d = context.space_data.region_3d
    
        if view3d.view_perspective == 'CAMERA':
            layout = self.layout
            layout.separator()
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
            layout.operator(AMTH_VIEW3D_OT_render_border_camera.bl_idname,
    
                            text="Camera as Render Border", icon="FULLSCREEN_ENTER")
    
    # //FEATURE: Camera Bounds as Render Border
    
    # FEATURE: Passepartout options on W menu
    def button_camera_passepartout(self, context):
    
        view3d = context.space_data.region_3d
        cam = context.scene.camera.data
    
        if view3d.view_perspective == 'CAMERA':
            layout = self.layout
            if cam.show_passepartout:
                layout.prop(cam, "passepartout_alpha", text="Passepartout")
            else:
                layout.prop(cam, "show_passepartout")
    
    # FEATURE: Show Only Render with Alt+Shift+Z
    
    Pablo Vazquez's avatar
    Pablo Vazquez committed
    class AMTH_VIEW3D_OT_show_only_render(Operator):
    
        bl_idname = "view3d.show_only_render"
        bl_label = "Show Only Render"
    
        def execute(self, context):
            space = bpy.context.space_data
    
            if space.show_only_render:
                space.show_only_render = False