# ##### 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, 8, 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 import bmesh from bpy.types import Operator, AddonPreferences, Panel, Menu from bpy.props import BoolProperty from mathutils import Vector from bpy.app.handlers import persistent from bl_operators.presets import AddPresetBase # 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 = 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") 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 = BoolProperty(default=False) node.status = BoolProperty(default=False) # Scene Debug # Cycles Node Types 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), ] scene.amaranth_cycles_node_types = bpy.props.EnumProperty( items=cycles_shader_node_types, name = "Shader") scene.amaranth_debug_scene_list_lamps = BoolProperty( default=False, name="Lamps List", description="Display a list of all the lamps") scene.amaranth_debug_scene_list_missing_images = BoolProperty( default=False, name="List Missing Images", description="Display a list of all the missing images") 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): 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 != '//': row = box.row() row.alignment = 'LEFT' props = row.operator( FILE_OT_directory_go_to.bl_idname, text=filepath, icon="BOOKMARKS", emboss=False) 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'} # 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(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): if context.space_data.tree_type == 'CompositorNodeTree': 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(Panel): '''Simplify Compositor Panel''' bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' bl_label = 'Simplify' 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') 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(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': for ou in no.outputs: if ou.links: 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: Mesh Symmetry Tools by Sergey Sharybin class MESH_OT_find_asymmetric(Operator): """ Find asymmetric vertices """ bl_idname = "mesh.find_asymmetric" bl_label = "Find Asymmetric" bl_options = {'UNDO', 'REGISTER'} @classmethod def poll(cls, context): object = context.object if object: return object.mode == 'EDIT' and object.type == 'MESH' return False def execute(self, context): threshold = 1e-6 object = context.object bm = bmesh.from_edit_mesh(object.data) # Deselect all the vertices for v in bm.verts: v.select = False for v1 in bm.verts: if abs(v1.co[0]) < threshold: continue mirror_found = False for v2 in bm.verts: if v1 == v2: continue if v1.co[0] * v2.co[0] > 0.0: continue mirror_coord = Vector(v2.co) mirror_coord[0] *= -1 if (mirror_coord - v1.co).length_squared < threshold: mirror_found = True break if not mirror_found: v1.select = True bm.select_flush_mode() bmesh.update_edit_mesh(object.data) return {'FINISHED'} class MESH_OT_make_symmetric(Operator): """ Make symmetric """ bl_idname = "mesh.make_symmetric" bl_label = "Make Symmetric" bl_options = {'UNDO', 'REGISTER'} @classmethod def poll(cls, context): object = context.object if object: return object.mode == 'EDIT' and object.type == 'MESH' return False def execute(self, context): threshold = 1e-6 object = context.object bm = bmesh.from_edit_mesh(object.data) for v1 in bm.verts: if v1.co[0] < threshold: continue if not v1.select: continue closest_vert = None closest_distance = -1 for v2 in bm.verts: if v1 == v2: continue if v2.co[0] > threshold: continue if not v2.select: continue mirror_coord = Vector(v2.co) mirror_coord[0] *= -1 distance = (mirror_coord - v1.co).length_squared if closest_vert is None or distance < closest_distance: closest_distance = distance closest_vert = v2 if closest_vert: closest_vert.select = False closest_vert.co = Vector(v1.co) closest_vert.co[0] *= -1 v1.select = False for v1 in bm.verts: if v1.select: closest_vert = None closest_distance = -1 for v2 in bm.verts: if v1 != v2: mirror_coord = Vector(v2.co) mirror_coord[0] *= -1 distance = (mirror_coord - v1.co).length_squared if closest_vert is None or distance < closest_distance: closest_distance = distance closest_vert = v2 if closest_vert: v1.select = False v1.co = Vector(closest_vert.co) v1.co[0] *= -1 bm.select_flush_mode() bmesh.update_edit_mesh(object.data) return {'FINISHED'} # // FEATURE: Mesh Symmetry Tools by Sergey Sharybin # FEATURE: Cycles Render Sampling Extra def render_cycles_scene_samples(self, context): layout = self.layout scenes = bpy.data.scenes scene = context.scene cscene = scene.cycles col = layout.column(align=True) if len(scene.render.layers) == 1 and \ scene.render.layers[0].samples == 0: pass else: col.separator() col.label(text="Samples Per RenderLayer:") for rl in scene.render.layers: row = col.row(align=True) row.label(rl.name) row.prop(rl, "samples", text="%s" % "Samples" if rl.samples > 0 else "Samples [Auto]") if (len(bpy.data.scenes) > 1): col.separator() col.label(text="Samples Per Scene:") row = col.row(align=True) if cscene.progressive == 'PATH': for s in bpy.data.scenes: if s != scene: row = col.row(align=True) if s.render.engine == 'CYCLES': cscene = s.cycles row.label(s.name) row.prop(cscene, "samples") else: row.label(text="Scene: '%s' is not using Cycles" % s.name) else: for s in bpy.data.scenes: if s != scene: row = col.row(align=True) if s.render.engine == 'CYCLES': cscene = s.cycles row.label(s.name) row.prop(cscene, "aa_samples", text="AA Samples") else: row.label(text="Scene: '%s' is not using Cycles" % s.name) # // FEATURE: Dupli Group Path # // FEATURE: Cycles Render Sampling Extra # FEATURE: Motion Paths Extras class POSE_OT_paths_clear_all(Operator): """Clear motion paths from all bones""" bl_idname = "pose.paths_clear_all" bl_label = "Clear All Motion Paths" bl_options = {'UNDO'} @classmethod def poll(cls, context): return context.mode == 'POSE' def execute(self, context): #silly but works for b in context.object.data.bones: b.select = True bpy.ops.pose.paths_clear() b.select = False return {'FINISHED'} class POSE_OT_paths_frame_match(Operator): """Match Start/End frame of scene to motion path range""" bl_idname = "pose.paths_frame_match" bl_label = "Match Frame Range" bl_options = {'UNDO'} def execute(self, context): avs = context.object.pose.animation_visualization scene = context.scene if avs.motion_path.type == 'RANGE': if scene.use_preview_range: avs.motion_path.frame_start = scene.frame_preview_start avs.motion_path.frame_end = scene.frame_preview_end else: avs.motion_path.frame_start = scene.frame_start avs.motion_path.frame_end = scene.frame_end else: if scene.use_preview_range: avs.motion_path.frame_before = scene.frame_preview_start avs.motion_path.frame_after = scene.frame_preview_end else: avs.motion_path.frame_before = scene.frame_start avs.motion_path.frame_after = scene.frame_end return {'FINISHED'} def pose_motion_paths_ui(self, context): layout = self.layout scene = context.scene avs = context.object.pose.animation_visualization if context.active_pose_bone: mpath = context.active_pose_bone.motion_path layout.separator() layout.label(text="Motion Paths Extras:") split = layout.split() col = split.column(align=True) if context.selected_pose_bones: if mpath: sub = col.row(align=True) sub.operator("pose.paths_update", text="Update Path", icon='BONE_DATA') sub.operator("pose.paths_clear", text="", icon='X') else: col.operator("pose.paths_calculate", text="Calculate Path", icon='BONE_DATA') else: col.label(text="Select Bones First", icon="ERROR") col = split.column(align=True) col.operator(POSE_OT_paths_frame_match.bl_idname, text="{}".format( "Set Preview Frame Range" if scene.use_preview_range else "Set Frame Range"), icon="{}".format("PREVIEW_RANGE" if scene.use_preview_range else "TIME")) col = layout.column() row = col.row(align=True) if avs.motion_path.type == 'RANGE': row.prop(avs.motion_path, "frame_start", text="Start") row.prop(avs.motion_path, "frame_end", text="End") else: row.prop(avs.motion_path, "frame_before", text="Before") row.prop(avs.motion_path, "frame_after", text="After") layout.separator() layout.operator(POSE_OT_paths_clear_all.bl_idname, icon="X") # // FEATURE: Motion Paths Extras # FEATURE: Final Render Resolution Display def render_final_resolution_ui(self, context): rd = context.scene.render layout = self.layout final_res_x = (rd.resolution_x * rd.resolution_percentage) / 100 final_res_y = (rd.resolution_y * rd.resolution_percentage) / 100 if rd.use_border: final_res_x_border = round((final_res_x * (rd.border_max_x - rd.border_min_x))) final_res_y_border = round((final_res_y * (rd.border_max_y - rd.border_min_y))) layout.label(text="Final Resolution: {} x {} [Border: {} x {}]".format( str(final_res_x)[:-2], str(final_res_y)[:-2], str(final_res_x_border), str(final_res_y_border))) else: layout.label(text="Final Resolution: {} x {}".format( str(final_res_x)[:-2], str(final_res_y)[:-2])) # // FEATURE: Final Render Resolution Display # FEATURE: Shader Nodes Extra Info def node_shader_extra(self, context): if context.space_data.tree_type == 'ShaderNodeTree': ob = context.active_object snode = context.space_data layout = self.layout if ob and snode.shader_type != 'WORLD': if ob.type == 'LAMP': layout.label(text="%s" % ob.name, icon="LAMP_%s" % ob.data.type) else: layout.label(text="%s" % ob.name, icon="OUTLINER_DATA_%s" % ob.type) # // FEATURE: Shader Nodes Extra Info # FEATURE: Scene Debug class SCENE_OT_cycles_shader_list_nodes(Operator): """List Cycles materials containing a specific shader""" bl_idname = "scene.cycles_list_nodes" bl_label = "List Materials" materials = [] @classmethod def poll(cls, context): return context.scene.render.engine == 'CYCLES' def execute(self, context): node_type = context.scene.amaranth_cycles_node_types roughness = False self.__class__.materials = [] print("\n=== Cycles Shader Type: %s === \n" % node_type) for ma in bpy.data.materials: if ma.node_tree: nodes = ma.node_tree.nodes for no in nodes: if no.type == node_type: for ou in no.outputs: if ou.links: if no.type in ['BSDF_GLOSSY','BSDF_DIFFUSE','BSDF_GLASS']: roughness = 'R: %.4f' % no.inputs['Roughness'].default_value else: roughness = False else: print('Note: \nOutput from "%s" node' % node_type, 'in material "%s"' % ma.name, 'not connected', '\n') self.__class__.materials = sorted(list(set(self.__class__.materials))) if ma.name not in self.__class__.materials: self.__class__.materials.append('%s%s [%s] %s%s' % ( '[L] ' if ma.library else '', ma.name, ma.users, '[F]' if ma.use_fake_user else '', ' - [%s]' % roughness if roughness else '')) if len(self.__class__.materials) == 0: self.report({"INFO"}, "No materials with nodes type %s found" % node_type) else: print("* A total of %d %s using %s were found \n" % ( len(self.__class__.materials), "material" if len(self.__class__.materials) == 1 else "materials", node_type)) count = 0 for mat in self.__class__.materials: print('%02d. %s' % (count+1, self.__class__.materials[count])) count += 1 print("\n") self.__class__.materials = sorted(list(set(self.__class__.materials))) return {'FINISHED'} class SCENE_OT_cycles_shader_list_nodes_clear(Operator): """Clear the list below""" bl_idname = "scene.cycles_list_nodes_clear" bl_label = "Clear Materials List" def execute(self, context): SCENE_OT_cycles_shader_list_nodes.materials[:] = [] print("* Cleared Cycles Materials List") return {'FINISHED'} class SCENE_OT_amaranth_debug_lamp_select(Operator): '''Select Lamp''' bl_idname = "scene.amaranth_debug_lamp_select" bl_label = "Select Lamp" lamp = bpy.props.StringProperty() def execute(self, context): if self.lamp: lamp = bpy.data.objects[self.lamp] bpy.ops.object.select_all(action='DESELECT') lamp.select = True context.scene.objects.active = lamp return{'FINISHED'} class SCENE_OT_list_missing_node_links(Operator): '''Print a list of missing node links''' bl_idname = "scene.list_missing_node_links" bl_label = "List Missing Node Links" count_groups = 0 count_images = 0 def execute(self, context): missing_groups = [] missing_images = [] libraries = [] self.__class__.count_groups = 0 self.__class__.count_images = 0 for ma in bpy.data.materials: if ma.node_tree: for no in ma.node_tree.nodes: if no.type == 'GROUP': if not no.node_tree: self.__class__.count_groups += 1 users_ngroup = [] for ob in bpy.data.objects: if ob.material_slots and ma.name in ob.material_slots: users_ngroup.append("%s%s%s" % ( "[L] " if ob.library else "", "[F] " if ob.use_fake_user else "", ob.name)) missing_groups.append("NG: %s%s%s [%s]%s%s\n" % ( "[L] " if ma.library else "", "[F] " if ma.use_fake_user else "", ma.name, ma.users, "\nLI: %s" % ma.library.filepath if ma.library else "", "\nOB: %s" % ', '.join(users_ngroup) if users_ngroup else "")) if ma.library: libraries.append(ma.library.filepath) if no.type == 'TEX_IMAGE': if no.image: import os.path image_path_exists = os.path.exists( bpy.path.abspath( no.image.filepath, library=no.image.library)) if not no.image or not image_path_exists: self.__class__.count_images += 1 users_images = [] for ob in bpy.data.objects: if ob.material_slots and ma.name in ob.material_slots: users_images.append("%s%s%s" % ( "[L] " if ob.library else "", "[F] " if ob.use_fake_user else "", ob.name)) missing_images.append("MA: %s%s%s [%s]%s%s%s%s\n" % ( "[L] " if ma.library else "", "[F] " if ma.use_fake_user else "", ma.name, ma.users, "\nLI: %s" % ma.library.filepath if ma.library else "", "\nIM: %s" % no.image.name if no.image else "", "\nLI: %s" % no.image.filepath if no.image and no.image.filepath else "", "\nOB: %s" % ', '.join(users_images) if users_images else "")) if ma.library: libraries.append(ma.library.filepath) # Remove duplicates and sort missing_groups = sorted(list(set(missing_groups))) missing_images = sorted(list(set(missing_images))) libraries = sorted(list(set(libraries))) print("\n\n== %s missing image %s and %s missing node %s ==" % ("No" if self.__class__.count_images == 0 else str(self.__class__.count_images), "node" if self.__class__.count_images == 1 else "nodes", "no" if self.__class__.count_groups == 0 else str(self.__class__.count_groups), "group" if self.__class__.count_groups == 1 else "groups")) # List Missing Node Groups if missing_groups: print("\n* Missing Node Group Links [NG]\n") for mig in missing_groups: print(mig) # List Missing Image Nodes if missing_images: print("\n* Missing Image Nodes Link [IM]\n") for mii in missing_images: print(mii) if missing_groups or missing_images: if libraries: print("\nThat's bad, run check on %s:" % ( "this library" if len(libraries) == 1 else "these libraries")) for li in libraries: print(li) else: self.report({"INFO"}, "Yay! No missing node links") print("\n") if missing_groups and missing_images: self.report({"WARNING"}, "%d missing image %s and %d missing node %s found" % (self.__class__.count_images, "node" if self.__class__.count_images == 1 else "nodes", self.__class__.count_groups, "group" if self.__class__.count_groups == 1 else "groups")) return{'FINISHED'} class SCENE_OT_list_missing_material_slots(Operator): '''List objects with empty material slots''' bl_idname = "scene.list_missing_material_slots" bl_label = "List Empty Material Slots" objects = [] def execute(self, context): self.__class__.objects = [] for ob in bpy.data.objects: for ma in ob.material_slots: if not ma.material: self.__class__.objects.append('%s%s' % ( '[L] ' if ob.library else '', ob.name)) self.__class__.objects = sorted(list(set(self.__class__.objects))) if len(self.__class__.objects) == 0: self.report({"INFO"}, "No objects with empty material slots found") else: print("\n* A total of %d %s with empty material slots was found \n" % ( len(self.__class__.objects), "object" if len(self.__class__.objects) == 1 else "objects")) count = 0 for obs in self.__class__.objects: print('%02d. %s' % (count+1, self.__class__.objects[count])) count += 1 print("\n") return{'FINISHED'} class SCENE_OT_list_missing_material_slots_clear(Operator): """Clear the list below""" bl_idname = "scene.list_missing_material_slots_clear" bl_label = "Clear Empty Material Slots List" def execute(self, context): SCENE_OT_list_missing_material_slots.objects[:] = [] print("* Cleared Empty Material Slots List") return {'FINISHED'} class SCENE_OT_blender_instance_open(Operator): '''Open in a new Blender instance''' bl_idname = "scene.blender_instance_open" bl_label = "Open Blender Instance" filepath = bpy.props.StringProperty() def execute(self, context): if self.filepath: filepath = bpy.path.abspath(self.filepath) import subprocess try: subprocess.Popen([bpy.app.binary_path, filepath]) except: print("Error on the new Blender instance") import traceback traceback.print_exc() return{'FINISHED'} class SCENE_PT_scene_debug(Panel): '''Scene Debug''' bl_label = 'Scene Debug' bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "scene" def draw(self, context): layout = self.layout scene = context.scene objects = bpy.data.objects ob_act = context.active_object images = bpy.data.images lamps = bpy.data.lamps images_missing = [] list_lamps = scene.amaranth_debug_scene_list_lamps list_missing_images = scene.amaranth_debug_scene_list_missing_images materials = SCENE_OT_cycles_shader_list_nodes.materials materials_count = len(SCENE_OT_cycles_shader_list_nodes.materials) missing_material_slots_obs = SCENE_OT_list_missing_material_slots.objects missing_material_slots_count = len(SCENE_OT_list_missing_material_slots.objects) engine = scene.render.engine # List Lamps box = layout.box() row = box.row(align=True) split = row.split() col = split.column() if lamps: row = col.row(align=True) row.alignment = 'LEFT' row.prop(scene, 'amaranth_debug_scene_list_lamps', icon="%s" % 'TRIA_DOWN' if list_lamps else 'TRIA_RIGHT', emboss=False) if objects and list_lamps: row = box.row(align=True) split = row.split(percentage=0.42) col = split.column() col.label(text="Name") split = split.split(percentage=0.1) col = split.column() col.label(text="", icon="BLANK1") if engine in ['CYCLES', 'BLENDER_RENDER']: if engine == 'BLENDER_RENDER': split = split.split(percentage=0.7) else: split = split.split(percentage=0.35) col = split.column() col.label(text="Samples") if engine == 'CYCLES': split = split.split(percentage=0.35) col = split.column() col.label(text="Size") split = split.split(percentage=0.8) col = split.column() col.label(text="Visibility") for ob in objects: if ob and ob.type == 'LAMP': lamp = ob.data clamp = ob.data.cycles row = box.row(align=True) split = row.split(percentage=0.5) col = split.column() row = col.row() row.alignment = 'LEFT' row.active = ob.name in context.scene.objects row.operator("scene.amaranth_debug_lamp_select", text='%s %s%s' % ( " [L] " if ob.library else "", ob.name, "" if ob.name in context.scene.objects else " [Not in Scene]"), icon="LAMP_%s" % ob.data.type, emboss=False).lamp = ob.name if engine == 'CYCLES': split = split.split(percentage=0.35) col = split.column() if scene.cycles.progressive == 'BRANCHED_PATH': col.prop(clamp, "samples", text="") if scene.cycles.progressive == 'PATH': col.label(text="N/A") if engine == 'BLENDER_RENDER': split = split.split(percentage=0.7) col = split.column() if lamp.type == 'HEMI': col.label(text="Not Available") elif lamp.type == 'AREA' and lamp.shadow_method == 'RAY_SHADOW': row = col.row(align=True) row.prop(lamp, "shadow_ray_samples_x", text="X") if lamp.shape == 'RECTANGLE': row.prop(lamp, "shadow_ray_samples_y", text="Y") elif lamp.shadow_method == 'RAY_SHADOW': col.prop(lamp, "shadow_ray_samples", text="Ray Samples") elif lamp.shadow_method == 'BUFFER_SHADOW': col.prop(lamp, "shadow_buffer_samples", text="Buffer Samples") else: col.label(text="No Shadow") if engine == 'CYCLES': split = split.split(percentage=0.4) col = split.column() if lamp.type in ['POINT','SUN', 'SPOT']: col.label(text="%.2f" % lamp.shadow_soft_size) elif lamp.type == 'HEMI': col.label(text="N/A") else: col.label(text="%.2f" % lamp.size) split = split.split(percentage=0.8) col = split.column() row = col.row(align=True) row.prop(ob, "hide", text="", emboss=False) row.prop(ob, "hide_render", text="", emboss=False) split = split.split(percentage=0.3) col = split.column() col.label(text="", icon="%s" % "TRIA_LEFT" if ob == ob_act else "BLANK1") else: row = col.row(align=True) row.alignment = 'LEFT' row.label(text="Lamps List", icon="RIGHTARROW_THIN") split = split.split() col = split.column() col.label(text="No Lamps", icon="LAMP_DATA") # List Missing Images box = layout.box() row = box.row(align=True) split = row.split() col = split.column() if images: import os.path for im in images: if im.type not in ['UV_TEST', 'RENDER_RESULT', 'COMPOSITING']: if not os.path.exists(bpy.path.abspath(im.filepath, library=im.library)): images_missing.append(["%s%s [%s]%s" % ( '[L] ' if im.library else '', im.name, im.users, ' [F]' if im.use_fake_user else ''), im.filepath if im.filepath else 'No Filepath', im.library.filepath if im.library else '']) if images_missing: row = col.row(align=True) row.alignment = 'LEFT' row.prop(scene, 'amaranth_debug_scene_list_missing_images', icon="%s" % 'TRIA_DOWN' if list_missing_images else 'TRIA_RIGHT', emboss=False) split = split.split() col = split.column() col.label(text="%s missing %s" % ( str(len(images_missing)), 'image' if len(images_missing) == 1 else 'images'), icon="ERROR") if list_missing_images: col = box.column(align=True) for mis in images_missing: col.label(text=mis[0], icon="IMAGE_DATA") col.label(text=mis[1], icon="LIBRARY_DATA_DIRECT") if mis[2]: row = col.row(align=True) row.alignment = "LEFT" row.operator(SCENE_OT_blender_instance_open.bl_idname, text=mis[2], icon="LINK_BLEND", emboss=False).filepath=mis[2] col.separator() else: row = col.row(align=True) row.alignment = 'LEFT' row.label(text="Great! No missing images", icon="RIGHTARROW_THIN") split = split.split() col = split.column() col.label(text="%s %s loading correctly" % ( str(len(images)), 'image' if len(images) == 1 else 'images'), icon="IMAGE_DATA") else: row = col.row(align=True) row.alignment = 'LEFT' row.label(text="No images loaded yet", icon="RIGHTARROW_THIN") # List Cycles Materials by Shader if engine == 'CYCLES': box = layout.box() split = box.split() col = split.column(align=True) col.prop(scene, 'amaranth_cycles_node_types', icon="MATERIAL") row = split.row(align=True) row.operator(SCENE_OT_cycles_shader_list_nodes.bl_idname, icon="SORTSIZE", text="List Materials Using Shader") if materials_count != 0: row.operator(SCENE_OT_cycles_shader_list_nodes_clear.bl_idname, icon="X", text="") col.separator() try: materials except NameError: pass else: if materials_count != 0: col = box.column(align=True) count = 0 col.label(text="%s %s found" % (materials_count, 'material' if materials_count == 1 else 'materials'), icon="INFO") for mat in materials: count += 1 col.label(text='%s' % (materials[count-1]), icon="MATERIAL") # List Missing Node Trees box = layout.box() row = box.row(align=True) split = row.split() col = split.column(align=True) split = col.split() split.label(text="Node Links") split.operator(SCENE_OT_list_missing_node_links.bl_idname, icon="NODETREE") if SCENE_OT_list_missing_node_links.count_groups != 0 or \ SCENE_OT_list_missing_node_links.count_images != 0: col.label(text="Warning! Check Console", icon="ERROR") if SCENE_OT_list_missing_node_links.count_groups != 0: col.label(text="%s" % ("%s node %s missing link" % ( str(SCENE_OT_list_missing_node_links.count_groups), "group" if SCENE_OT_list_missing_node_links.count_groups == 1 else "groups")), icon="NODETREE") if SCENE_OT_list_missing_node_links.count_images != 0: col.label(text="%s" % ("%s image %s missing link" % ( str(SCENE_OT_list_missing_node_links.count_images), "node" if SCENE_OT_list_missing_node_links.count_images == 1 else "nodes")), icon="IMAGE_DATA") col.separator() # List Empty Materials Slots box = layout.box() split = box.split() col = split.column(align=True) col.label(text="Material Slots") row = split.row(align=True) row.operator(SCENE_OT_list_missing_material_slots.bl_idname, icon="MATERIAL", text="List Empty Materials Slots") if missing_material_slots_count != 0: row.operator(SCENE_OT_list_missing_material_slots_clear.bl_idname, icon="X", text="") col.separator() try: missing_material_slots_obs except NameError: pass else: if missing_material_slots_count != 0: col = box.column(align=True) count = 0 col.label(text="%s %s with empty material slots found" % ( missing_material_slots_count, 'object' if missing_material_slots_count == 1 else 'objects'), icon="INFO") for obs in missing_material_slots_obs: count += 1 col.label(text='%s' % ( missing_material_slots_obs[count-1]), icon="OBJECT_DATA") # // FEATURE: Scene Debug # FEATURE: Dupli Group Path def ui_dupli_group_library_path(self, context): ob = context.object lib = ob.dupli_group.library.filepath row = self.layout.row() row.alignment = 'LEFT' if ob and ob.dupli_group and ob.dupli_group.library: row.operator(SCENE_OT_blender_instance_open.bl_idname, text="Library: %s" % lib, emboss=False, icon="LINK_BLEND").filepath=lib # // FEATURE: Dupli Group Path # FEATURE: Color Management Presets class SCENE_MT_color_management_presets(Menu): """List of Color Management presets""" bl_label = "Color Management Presets" preset_subdir = "color" preset_operator = "script.execute_preset" draw = Menu.draw_preset class AddPresetColorManagement(AddPresetBase, Operator): """Add or remove a Color Management preset""" bl_idname = "scene.color_management_preset_add" bl_label = "Add Color Management Preset" preset_menu = "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) row.menu("SCENE_MT_color_management_presets", text=bpy.types.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 classes = (SCENE_MT_color_management_presets, AddPresetColorManagement, SCENE_PT_scene_debug, SCENE_OT_refresh, SCENE_OT_cycles_shader_list_nodes, SCENE_OT_cycles_shader_list_nodes_clear, SCENE_OT_amaranth_debug_lamp_select, SCENE_OT_list_missing_node_links, SCENE_OT_list_missing_material_slots, SCENE_OT_list_missing_material_slots_clear, SCENE_OT_blender_instance_open, WM_OT_save_reload, MESH_OT_find_asymmetric, MESH_OT_make_symmetric, 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, POSE_OT_paths_clear_all, POSE_OT_paths_frame_match, 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) 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) 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) 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') kmi.properties.name = "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) 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) 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) 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) 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()