Skip to content
Snippets Groups Projects
scene_amaranth_toolset.py 33.5 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",
        "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin",
    
        "version": (0, 7),
        "blender": (2, 69),
    
        "location": "Scene Properties > Amaranth Toolset Panel",
        "description": "A collection of tools and settings to improve productivity",
        "warning": "",
        "wiki_url": "http://pablovazquez.org/amaranth",
        "tracker_url": "",
        "category": "Scene"}
    
    
    import bpy
    
    from bpy.types import Operator, AddonPreferences, Panel
    
    from bpy.props import BoolProperty
    from mathutils import Vector
    from bpy.app.handlers import persistent
    
    # 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,
                )
    
    
        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")
    
            sub.separator()
    
            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 = bpy.props.BoolProperty(
            default=False,
            name="Unsimplify Render",
            description="Disable Simplify during render")
        scene.simplify_status = bpy.props.BoolProperty(default=False)
    
        node.use_matching_indices = bpy.props.BoolProperty(
            default=True,
            description="If disabled, display all available indices")
    
        test_items = [
            ("ALL", "All Types", "", 0),
            ("BLUR", "Blur", "", 1),
            ("BOKEHBLUR", "Bokeh Blur", "", 2),
            ("VECBLUR", "Vector Blur", "", 3),
            ("DEFOCUS", "Defocus", "", 4),
            ("R_LAYERS", "Render Layer", "", 5)
            ]
    
        nodes_compo.types = bpy.props.EnumProperty(
            items=test_items, name = "Types")
    
        nodes_compo.toggle_mute = bpy.props.BoolProperty(default=False)
        node.status = bpy.props.BoolProperty(default=False)
    
    
    def clear_properties():
        props = (
            "use_unsimplify_render",
            "simplify_status",
            "use_matching_indices",
            "use_simplify_nodes_vector",
            "status"
        )
        
        wm = bpy.context.window_manager
        for p in props:
            if p in wm:
                del wm[p]
    
    # FEATURE: Refresh Scene!
    class 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(
                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")
    
    class 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(
                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)
    
            # 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
    class 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(
                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
    class 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
            import os
    
            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()
           
            if libslist:
                for filepath in libslist:
                    if filepath != '//':
                        split = box.split(percentage=0.85)
                        col = split.column()
                        sub = col.column(align=True)
                        sub.label(text=filepath)
                
                        col = split.column()
                        sub = col.column(align=True)
                        props = sub.operator(
                            FILE_OT_directory_go_to.bl_idname,
                            text="", icon="BOOKMARKS")
                        props.filepath = filepath
            else:
                box.label(text='No libraries loaded')
    
    class 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'}
        
    
    351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
    # FEATURE: Node Templates
    class 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
    
            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])
    
            if tree.nodes.active:
                blur.location = tree.nodes.active.location
                blur.location += Vector((330.0, -250.0))
            else:
                blur.location += Vector((space.cursor_location[0], space.cursor_location[1]))
    
            ellipse.location = blur.location
            ellipse.location += Vector((-300.0, 0))
    
            ramp.location = blur.location
            ramp.location += Vector((175.0, 0))
    
            overlay.location = ramp.location
            overlay.location += Vector((240.0, 275.0))
    
            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
            frame.color = (0.783538, 0.0241576, 0.0802198)
            
            overlay.parent = None
            overlay.label = 'Vignette Overlay'
    
        def execute(self, context):
            self._setupNodes(context)
    
            return {'FINISHED'}
    
    # Node Templates Menu
    class NODE_MT_amaranth_templates(bpy.types.Menu):
        bl_idname = '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
            layout.operator(
                NODE_OT_AddTemplateVignette.bl_idname,
                text="Vignette",
                icon='COLOR')
    
    def node_templates_pulldown(self, context):
        layout = self.layout
        row = layout.row(align=True)
        row.scale_x = 1.3
        row.menu("NODE_MT_amaranth_templates",
            icon="RADIO")
    # // 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
    class NODE_PT_simplify(bpy.types.Panel):
        '''Simplify Compositor Panel'''
        bl_space_type = 'NODE_EDITOR'
        bl_region_type = 'UI'
        bl_label = 'Simplify'
    #    bl_options = {'DEFAULT_CLOSED'}
    
        def draw(self, context):
            layout = self.layout
            node_tree = context.scene.node_tree
    
            if node_tree is not None:
                layout.prop(node_tree, 'types')
                layout.operator(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")
    
    class 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'}
            
    
    # FEATURE: OB/MA ID panel in Node Editor
    class NODE_PT_indices(bpy.types.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
        
            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':
                                        meshlights = meshlights + 1
                                        if ob in context.visible_objects:
                                            meshlights_visible = meshlights_visible + 1
                                        break
                if ob in context.selected_objects:
                    if ob.type == 'CAMERA':
                        cameras_selected = 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
    class 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()
            layout.operator(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
    class 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
            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"}
    
    class 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
    class 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:
                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 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):
        
        if context.scene.render.engine == 'CYCLES':
            self.layout.operator('object.select_meshlights', icon="LAMP_SUN")
    # // FEATURE: Select Meshlights
    
    # FEATURE: Cycles Viewport Extra Settings
    def material_cycles_settings_extra(self, context):
        
        layout = self.layout
        col = layout.column()
        row = col.row(align=True)
        
        obj = context.object
        mat = context.material
        if obj.type == 'MESH':
            row.prop(obj, "show_transparent", text="Viewport Alpha")
            row.active = obj.show_transparent
            row.prop(mat, "alpha", text="Alpha")
    
    # // FEATURE: Cycles Viewport Extra Settings
    
    # FEATURE: Particles Material indicator
    def particles_material_info(self, context):
    
        layout = self.layout
    
        ob = context.object
        psys = context.particle_system
    
        layout.label(
            text="Material: %s" % ob.material_slots[psys.settings.material-1].name \
                if psys.settings.material <= len(ob.material_slots) \
                else "No material with this index. Using '{}'".format( \
                    ob.material_slots[len(ob.material_slots)-1].name))
    # // FEATURE: Particles Material indicator
    
    
    classes = (SCENE_OT_refresh,
               WM_OT_save_reload,
               NODE_OT_AddTemplateVignette,
               NODE_MT_amaranth_templates,
               FILE_OT_directory_current_blend,
    
               FILE_OT_directory_go_to,
    
               NODE_PT_indices,
               NODE_PT_simplify,
               NODE_OT_toggle_mute,
               NODE_OT_show_active_node_image,
               VIEW3D_OT_render_border_camera,
               VIEW3D_OT_show_only_render,
    
               OBJECT_OT_select_meshlights,
               FILE_PT_libraries)
    
    
    addon_keymaps = []
    
    kmi_defs = (
        ('wm.call_menu', 'W', False, False, False, (('name', NODE_MT_amaranth_templates.bl_idname),)),
    )
    
    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.CyclesMaterial_PT_settings.append(material_cycles_settings_extra)
    
        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.PARTICLE_PT_render.prepend(particles_material_info)
    
    
        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='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)
            kmi = km.keymap_items.new('wm.context_toggle_enum', 'Z', 'PRESS', shift=True, alt=False)
            kmi.properties.data_path = 'space_data.viewport_shade'
            kmi.properties.value_1 = 'SOLID'
            kmi.properties.value_2 = 'RENDERED'
    
            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')
    
            addon_keymaps.append((km, kmi))
    
            # copypasted from the awesome node efficiency tools, future hotkeys proof!
            km = kc.keymaps.new(name='Node Editor', space_type="NODE_EDITOR")
            for (identifier, key, CTRL, SHIFT, ALT, props) in kmi_defs:
                kmi = km.keymap_items.new(identifier, key, 'PRESS', ctrl=CTRL, shift=SHIFT, alt=ALT)
                if props:
                    for prop, value in props:
                        setattr(kmi.properties, prop, value)
                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.CyclesMaterial_PT_settings.remove(material_cycles_settings_extra)
    
        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.PARTICLE_PT_render.remove(particles_material_info)
    
    
        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()