Amaranth 0.9 Lil tweaks/fixes
[blender-addons-contrib.git] / scene_amaranth_toolset.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 bl_info = {
20     "name": "Amaranth Toolset",
21     "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne",
22     "version": (0, 9, 0),
23     "blender": (2, 70),
24     "location": "Everywhere!",
25     "description": "A collection of tools and settings to improve productivity",
26     "warning": "",
27     "wiki_url": "http://pablovazquez.org/amaranth",
28     "tracker_url": "",
29     "category": "Scene"}
30
31
32 import bpy
33 import bmesh
34 from bpy.types import Operator, AddonPreferences, Panel, Menu
35 from bpy.props import (BoolProperty, EnumProperty,
36                        FloatProperty, IntProperty,
37                        StringProperty)
38 from mathutils import Vector
39 from bpy.app.handlers import persistent
40 from bl_operators.presets import AddPresetBase
41
42 # Preferences
43 class AmaranthToolsetPreferences(AddonPreferences):
44     bl_idname = __name__
45     use_frame_current = BoolProperty(
46             name="Current Frame Slider",
47             description="Set the current frame from the Specials menu in the 3D View",
48             default=True,
49             )
50     use_file_save_reload = BoolProperty(
51             name="Save & Reload File",
52             description="File menu > Save & Reload, or Ctrl + Shift + W",
53             default=True,
54             )
55
56     use_scene_refresh = BoolProperty(
57             name="Refresh Scene",
58             description="Specials Menu [W], or hit F5",
59             default=True,
60             )
61     use_timeline_extra_info = BoolProperty(
62             name="Timeline Extra Info",
63             description="Timeline Header",
64             default=True,
65             )
66     use_image_node_display = BoolProperty(
67             name="Active Image Node in Editor",
68             description="Display active node image in image editor",
69             default=True,
70             )
71     use_scene_stats = BoolProperty(
72             name="Extra Scene Statistics",
73             description="Display extra scene statistics in Info editor's header",
74             default=True,
75             )
76
77     frames_jump = IntProperty(
78                 name="Frames",
79                 description="Number of frames to jump forward/backward",
80                 default=10,
81                 min=1)
82
83
84     def draw(self, context):
85         layout = self.layout
86
87         layout.label(
88             text="Here you can enable or disable specific tools, "
89                  "in case they interfere with others or are just plain annoying")
90
91         split = layout.split(percentage=0.25)
92
93         col = split.column()
94         sub = col.column(align=True)
95         sub.label(text="3D View", icon="VIEW3D")
96         sub.prop(self, "use_frame_current")
97         sub.prop(self, "use_scene_refresh")
98
99         sub.separator()
100
101         sub.label(text="General", icon="SCENE_DATA")
102         sub.prop(self, "use_file_save_reload")
103         sub.prop(self, "use_timeline_extra_info")
104         sub.prop(self, "use_scene_stats")
105
106         sub.separator()
107
108         sub.label(text="Nodes Editor", icon="NODETREE")
109         sub.prop(self, "use_image_node_display")
110
111         col = split.column()
112         sub = col.column(align=True)
113         sub.label(text="")
114         sub.label(
115             text="Set the current frame from the Specials menu in the 3D View [W]")
116         sub.label(
117             text="Refresh the current Scene. Hotkey: F5 or in Specials menu [W]")
118
119         sub.separator()
120         sub.label(text="") # General
121         sub.label(
122             text="Quickly save and reload the current file (no warning!). "
123                  "File menu or Ctrl+Shift+W")
124         sub.label(
125             text="SMPTE Timecode and frames left/ahead on Timeline's header")
126         sub.label(
127             text="Display extra statistics for Scenes, Cameras, and Meshlights (Cycles)")
128
129         sub.separator()
130         sub.label(text="") # Nodes
131         sub.label(
132             text="When selecting an Image node, display it on the Image editor "
133                  "(if any)")
134
135 # Properties
136 def init_properties():
137
138     scene = bpy.types.Scene
139     node = bpy.types.Node
140     nodes_compo = bpy.types.CompositorNodeTree
141
142     scene.use_unsimplify_render = BoolProperty(
143         default=False,
144         name="Unsimplify Render",
145         description="Disable Simplify during render")
146     scene.simplify_status = BoolProperty(default=False)
147
148     node.use_matching_indices = BoolProperty(
149         default=True,
150         description="If disabled, display all available indices")
151
152     nodes_compo_types = [
153         ("ALL", "All Types", "", 0),
154         ("BLUR", "Blur", "", 1),
155         ("BOKEHBLUR", "Bokeh Blur", "", 2),
156         ("VECBLUR", "Vector Blur", "", 3),
157         ("DEFOCUS", "Defocus", "", 4),
158         ("R_LAYERS", "Render Layer", "", 5)
159         ]
160
161     nodes_compo.types = EnumProperty(
162         items=nodes_compo_types, name = "Types")
163
164     nodes_compo.toggle_mute = BoolProperty(default=False)
165     node.status = BoolProperty(default=False)
166
167     # Scene Debug
168     # Cycles Node Types
169     cycles_shader_node_types = [
170         ("BSDF_DIFFUSE", "Diffuse BSDF", "", 0),
171         ("BSDF_GLOSSY", "Glossy BSDF", "", 1),
172         ("BSDF_TRANSPARENT", "Transparent BSDF", "", 2),
173         ("BSDF_REFRACTION", "Refraction BSDF", "", 3),
174         ("BSDF_GLASS", "Glass BSDF", "", 4),
175         ("BSDF_TRANSLUCENT", "Translucent BSDF", "", 5),
176         ("BSDF_ANISOTROPIC", "Anisotropic BSDF", "", 6),
177         ("BSDF_VELVET", "Velvet BSDF", "", 7),
178         ("BSDF_TOON", "Toon BSDF", "", 8),
179         ("SUBSURFACE_SCATTERING", "Subsurface Scattering", "", 9),
180         ("EMISSION", "Emission", "", 10),
181         ("BSDF_HAIR", "Hair BSDF", "", 11),
182         ("BACKGROUND", "Background", "", 12),
183         ("AMBIENT_OCCLUSION", "Ambient Occlusion", "", 13),
184         ("HOLDOUT", "Holdout", "", 14),
185         ("VOLUME_ABSORPTION", "Volume Absorption", "", 15),
186         ("VOLUME_SCATTER", "Volume Scatter", "", 16)
187         ]
188
189     scene.amaranth_cycles_node_types = EnumProperty(
190         items=cycles_shader_node_types, name = "Shader")
191
192     scene.amaranth_lighterscorner_list_meshlights = BoolProperty(
193         default=False,
194         name="List Meshlights",
195         description="Include light emitting meshes on the list")
196
197     scene.amaranth_debug_scene_list_missing_images = BoolProperty(
198         default=False,
199         name="List Missing Images",
200         description="Display a list of all the missing images")
201
202     scene.amaranth_cycles_list_sampling = BoolProperty(
203         default=False,
204         name="Samples Per:")
205
206     bpy.types.ShaderNodeNormal.normal_vector = prop_normal_vector
207     bpy.types.CompositorNodeNormal.normal_vector = prop_normal_vector
208     
209     bpy.types.CyclesRenderSettings.use_samples_final = BoolProperty(
210         name="Use Final Render Samples",
211         description="Use current shader samples as final render samples",
212         default=False)
213
214 def clear_properties():
215     props = (
216         "use_unsimplify_render",
217         "simplify_status",
218         "use_matching_indices",
219         "use_simplify_nodes_vector",
220         "status",
221         "types",
222         "toggle_mute",
223         "amaranth_cycles_node_types",
224         "amaranth_lighterscorner_list_meshlights",
225         "amaranth_debug_scene_list_missing_images",
226         "amarath_cycles_list_sampling",
227         "normal_vector",
228         "use_samples_final"
229     )
230     
231     wm = bpy.context.window_manager
232     for p in props:
233         if p in wm:
234             del wm[p]
235
236 # Some settings are bound to be saved on a startup py file
237 def amaranth_text_startup(context):
238
239     amth_text_name = "AmaranthStartup.py"
240     amth_text_exists = False
241
242     global amth_text
243
244     try:
245         if bpy.data.texts:
246             for tx in bpy.data.texts:
247                 if tx.name == amth_text_name:
248                     amth_text_exists = True
249                     amth_text = bpy.data.texts[amth_text_name]
250                     break
251                 else:
252                     amth_text_exists = False
253
254         if not amth_text_exists:
255             bpy.ops.text.new()
256             amth_text = bpy.data.texts[-1]
257             amth_text.name = amth_text_name
258             amth_text.write("# Amaranth Startup Script\nimport bpy\n\n")
259             amth_text.use_module = True
260
261         return amth_text_exists
262     except AttributeError:
263         return None
264
265 # Is Emission Material? For select and stats
266 def cycles_is_emission(context, ob):
267
268     is_emission = False
269
270     if ob.material_slots:
271         for ma in ob.material_slots:
272             if ma.material:
273                 if ma.material.node_tree and ma.material.node_tree.nodes:
274                     for no in ma.material.node_tree.nodes:
275                         if no.type in {'EMISSION', 'GROUP'}:
276                             for ou in no.outputs:
277                                 if ou.links:
278                                     if no.type == 'GROUP' and no.node_tree and no.node_tree.nodes:
279                                         for gno in no.node_tree.nodes:
280                                             if gno.type == 'EMISSION':
281                                                 for gou in gno.outputs:
282                                                     if ou.links and gou.links:
283                                                         is_emission = True
284
285                                     elif no.type == 'EMISSION':
286                                         if ou.links:
287                                             is_emission = True
288     return is_emission
289
290 # FEATURE: Refresh Scene!
291 class AMTH_SCENE_OT_refresh(Operator):
292     """Refresh the current scene"""
293     bl_idname = "scene.refresh"
294     bl_label = "Refresh!"
295     
296     def execute(self, context):
297         preferences = context.user_preferences.addons[__name__].preferences
298         scene = context.scene
299
300         if preferences.use_scene_refresh:    
301             # Changing the frame is usually the best way to go
302             scene.frame_current = scene.frame_current
303             self.report({"INFO"}, "Scene Refreshed!")
304             
305         return {'FINISHED'}
306
307 def button_refresh(self, context):
308
309     preferences = context.user_preferences.addons[__name__].preferences
310
311     if preferences.use_scene_refresh:
312         self.layout.separator()
313         self.layout.operator(
314             AMTH_SCENE_OT_refresh.bl_idname,
315             text="Refresh!",
316             icon='FILE_REFRESH')
317 # // FEATURE: Refresh Scene!
318
319 # FEATURE: Save & Reload
320 def save_reload(self, context, path):
321
322     if path:
323         bpy.ops.wm.save_mainfile()
324         self.report({'INFO'}, "Saved & Reloaded")
325         bpy.ops.wm.open_mainfile("EXEC_DEFAULT", filepath=path)
326     else:
327         bpy.ops.wm.save_as_mainfile("INVOKE_AREA")
328
329 class AMTH_WM_OT_save_reload(Operator):
330     """Save and Reload the current blend file"""
331     bl_idname = "wm.save_reload"
332     bl_label = "Save & Reload"
333
334     def execute(self, context):
335
336         path = bpy.data.filepath
337         save_reload(self, context, path)
338         return {'FINISHED'}
339
340 def button_save_reload(self, context):
341
342     preferences = context.user_preferences.addons[__name__].preferences
343
344     if preferences.use_file_save_reload:
345         self.layout.separator()
346         self.layout.operator(
347             AMTH_WM_OT_save_reload.bl_idname,
348             text="Save & Reload",
349             icon='FILE_REFRESH')
350 # // FEATURE: Save & Reload
351
352 # FEATURE: Current Frame
353 def button_frame_current(self, context):
354
355     preferences = context.user_preferences.addons[__name__].preferences
356     scene = context.scene
357
358     if preferences.use_frame_current:
359         self.layout.separator()
360         self.layout.prop(
361             scene, "frame_current",
362             text="Set Current Frame")
363 # // FEATURE: Current Frame
364
365 # FEATURE: Timeline Time + Frames Left
366 def label_timeline_extra_info(self, context):
367
368     preferences = context.user_preferences.addons[__name__].preferences
369     layout = self.layout
370     scene = context.scene
371
372     if preferences.use_timeline_extra_info:
373         row = layout.row(align=True)
374
375         # Check for preview range
376         frame_start = scene.frame_preview_start if scene.use_preview_range else scene.frame_start
377         frame_end = scene.frame_preview_end if scene.use_preview_range else scene.frame_end
378         
379         row.label(text="%s / %s" % (bpy.utils.smpte_from_frame(scene.frame_current - frame_start),
380                         bpy.utils.smpte_from_frame(frame_end - frame_start)))
381
382         if (scene.frame_current > frame_end):
383             row.label(text="%s Frames Ahead" % ((frame_end - scene.frame_current) * -1))
384         elif (scene.frame_current == frame_start):
385             row.label(text="Start Frame (%s left)" % (frame_end - scene.frame_current))
386         elif (scene.frame_current == frame_end):
387             row.label(text="%s End Frame" % scene.frame_current)
388         else:
389             row.label(text="%s Frames Left" % (frame_end - scene.frame_current))
390
391 # // FEATURE: Timeline Time + Frames Left
392
393 # FEATURE: Directory Current Blend
394 class AMTH_FILE_OT_directory_current_blend(Operator):
395     """Go to the directory of the currently open blend file"""
396     bl_idname = "file.directory_current_blend"
397     bl_label = "Current Blend's Folder"
398
399     def execute(self, context):
400         bpy.ops.file.select_bookmark(dir='//')
401         return {'FINISHED'}
402
403 def button_directory_current_blend(self, context):
404
405     if bpy.data.filepath:
406         self.layout.operator(
407             AMTH_FILE_OT_directory_current_blend.bl_idname,
408             text="Current Blend's Folder",
409             icon='APPEND_BLEND')
410 # // FEATURE: Directory Current Blend
411
412 # FEATURE: Libraries panel on file browser
413 class AMTH_FILE_PT_libraries(Panel):
414     bl_space_type = 'FILE_BROWSER'
415     bl_region_type = 'CHANNELS'
416     bl_label = "Libraries"
417
418     def draw(self, context):
419         layout = self.layout
420
421         libs = bpy.data.libraries
422         libslist = []
423
424         # Build the list of folders from libraries
425         import os.path
426
427         for lib in libs:
428             directory_name = os.path.dirname(lib.filepath)
429             libslist.append(directory_name)
430
431         # Remove duplicates and sort by name
432         libslist = set(libslist)
433         libslist = sorted(libslist)
434
435         # Draw the box with libs
436
437         row = layout.row()
438         box = row.box()
439
440         if libslist:
441             col = box.column()
442             for filepath in libslist:
443                 if filepath != '//':
444                     row = col.row()
445                     row.alignment = 'LEFT'
446                     props = row.operator(
447                         AMTH_FILE_OT_directory_go_to.bl_idname,
448                         text=filepath, icon="BOOKMARKS",
449                         emboss=False)
450                     props.filepath = filepath
451         else:
452             box.label(text='No libraries loaded')
453
454 class AMTH_FILE_OT_directory_go_to(Operator):
455     """Go to this library's directory"""
456     bl_idname = "file.directory_go_to"
457     bl_label = "Go To"
458
459     filepath = bpy.props.StringProperty(subtype="FILE_PATH")
460
461     def execute(self, context):
462         bpy.ops.file.select_bookmark(dir=self.filepath)
463         return {'FINISHED'}
464
465 # FEATURE: Node Templates
466 class AMTH_NODE_OT_AddTemplateVignette(Operator):
467     bl_idname = "node.template_add_vignette"
468     bl_label = "Add Vignette"
469     bl_description = "Add a vignette effect"
470     bl_options = {'REGISTER', 'UNDO'}
471
472     @classmethod
473     def poll(cls, context):
474         space = context.space_data
475         return space.type == 'NODE_EDITOR' \
476                 and space.node_tree is not None \
477                 and space.tree_type == 'CompositorNodeTree'
478
479     # used as reference the setup scene script from master nazgul
480     def _setupNodes(self, context):
481         scene = context.scene
482         space = context.space_data
483         tree = scene.node_tree
484
485         bpy.ops.node.select_all(action='DESELECT')
486
487         ellipse = tree.nodes.new(type='CompositorNodeEllipseMask')
488         ellipse.width = 0.8
489         ellipse.height = 0.4
490         blur = tree.nodes.new(type='CompositorNodeBlur')
491         blur.use_relative = True
492         blur.factor_x = 30
493         blur.factor_y = 50
494         ramp = tree.nodes.new(type='CompositorNodeValToRGB')
495         ramp.color_ramp.interpolation = 'B_SPLINE'
496         ramp.color_ramp.elements[1].color = (0.6, 0.6, 0.6, 1)
497
498         overlay = tree.nodes.new(type='CompositorNodeMixRGB')
499         overlay.blend_type = 'OVERLAY'
500         overlay.inputs[0].default_value = 0.8
501         overlay.inputs[1].default_value = (0.5, 0.5, 0.5, 1)
502
503         tree.links.new(ellipse.outputs["Mask"],blur.inputs["Image"])
504         tree.links.new(blur.outputs["Image"],ramp.inputs[0])
505         tree.links.new(ramp.outputs["Image"],overlay.inputs[2])
506
507         if tree.nodes.active:
508             blur.location = tree.nodes.active.location
509             blur.location += Vector((330.0, -250.0))
510         else:
511             blur.location += Vector((space.cursor_location[0], space.cursor_location[1]))
512
513         ellipse.location = blur.location
514         ellipse.location += Vector((-300.0, 0))
515
516         ramp.location = blur.location
517         ramp.location += Vector((175.0, 0))
518
519         overlay.location = ramp.location
520         overlay.location += Vector((240.0, 275.0))
521
522         for node in {ellipse, blur, ramp, overlay}:
523             node.select = True
524             node.show_preview = False
525
526         bpy.ops.node.join()
527
528         frame = ellipse.parent
529         frame.label = 'Vignette'
530         frame.use_custom_color = True
531         frame.color = (0.783538, 0.0241576, 0.0802198)
532         
533         overlay.parent = None
534         overlay.label = 'Vignette Overlay'
535
536     def execute(self, context):
537         self._setupNodes(context)
538
539         return {'FINISHED'}
540
541 # Node Templates Menu
542 class AMTH_NODE_MT_amaranth_templates(Menu):
543     bl_idname = 'AMTH_NODE_MT_amaranth_templates'
544     bl_space_type = 'NODE_EDITOR'
545     bl_label = "Templates"
546     bl_description = "List of Amaranth Templates"
547
548     def draw(self, context):
549         layout = self.layout
550         layout.operator(
551             AMTH_NODE_OT_AddTemplateVignette.bl_idname,
552             text="Vignette",
553             icon='COLOR')
554
555 def node_templates_pulldown(self, context):
556     if context.space_data.tree_type == 'CompositorNodeTree':
557         layout = self.layout
558         row = layout.row(align=True)
559         row.scale_x = 1.3
560         row.menu("AMTH_NODE_MT_amaranth_templates",
561             icon="NODETREE")
562 # // FEATURE: Node Templates
563
564 def node_stats(self,context):
565     if context.scene.node_tree:
566         tree_type = context.space_data.tree_type
567         nodes = context.scene.node_tree.nodes
568         nodes_total = len(nodes.keys())
569         nodes_selected = 0
570         for n in nodes:
571             if n.select:
572                 nodes_selected = nodes_selected + 1
573
574         if tree_type == 'CompositorNodeTree':
575             layout = self.layout
576             row = layout.row(align=True)
577             row.label(text="Nodes: %s/%s" % (nodes_selected, str(nodes_total)))
578
579 # FEATURE: Simplify Compo Nodes
580 class AMTH_NODE_PT_simplify(Panel):
581     '''Simplify Compositor Panel'''
582     bl_space_type = 'NODE_EDITOR'
583     bl_region_type = 'UI'
584     bl_label = 'Simplify'
585     bl_options = {'DEFAULT_CLOSED'}
586
587     @classmethod
588     def poll(cls, context):
589         space = context.space_data
590         return space.type == 'NODE_EDITOR' \
591                 and space.node_tree is not None \
592                 and space.tree_type == 'CompositorNodeTree'
593
594     def draw(self, context):
595         layout = self.layout
596         node_tree = context.scene.node_tree
597
598         if node_tree is not None:
599             layout.prop(node_tree, 'types')
600             layout.operator(AMTH_NODE_OT_toggle_mute.bl_idname,
601                 text="Turn On" if node_tree.toggle_mute else "Turn Off",
602                 icon='RESTRICT_VIEW_OFF' if node_tree.toggle_mute else 'RESTRICT_VIEW_ON')
603         
604             if node_tree.types == 'VECBLUR':
605                 layout.label(text="This will also toggle the Vector pass {}".format(
606                                     "on" if node_tree.toggle_mute else "off"), icon="INFO")
607
608 class AMTH_NODE_OT_toggle_mute(Operator):
609     """"""
610     bl_idname = "node.toggle_mute"
611     bl_label = "Toggle Mute"
612
613     def execute(self, context):
614         scene = context.scene
615         node_tree = scene.node_tree
616         node_type = node_tree.types
617         rlayers = scene.render
618         
619         if not 'amaranth_pass_vector' in scene.keys():
620             scene['amaranth_pass_vector'] = []
621         
622         #can't extend() the list, so make a dummy one
623         pass_vector = scene['amaranth_pass_vector']
624
625         if not pass_vector:
626             pass_vector = []
627
628         if node_tree.toggle_mute:
629             for node in node_tree.nodes:
630                 if node_type == 'ALL':
631                     node.mute = node.status
632                 if node.type == node_type:
633                     node.mute = node.status
634                 if node_type == 'VECBLUR':
635                     for layer in rlayers.layers:
636                         if layer.name in pass_vector:
637                             layer.use_pass_vector = True
638                             pass_vector.remove(layer.name)
639
640                 node_tree.toggle_mute = False
641
642         else:
643             for node in node_tree.nodes:
644                 if node_type == 'ALL':
645                     node.mute = True
646                 if node.type == node_type:
647                     node.status = node.mute
648                     node.mute = True
649                 if node_type == 'VECBLUR':
650                     for layer in rlayers.layers:
651                         if layer.use_pass_vector:
652                             pass_vector.append(layer.name)
653                             layer.use_pass_vector = False
654                             pass
655
656                 node_tree.toggle_mute = True
657
658         # Write back to the custom prop
659         pass_vector = sorted(set(pass_vector))
660         scene['amaranth_pass_vector'] = pass_vector
661
662         return {'FINISHED'}
663         
664
665 # FEATURE: OB/MA ID panel in Node Editor
666 class AMTH_NODE_PT_indices(Panel):
667     '''Object / Material Indices Panel'''
668     bl_space_type = 'NODE_EDITOR'
669     bl_region_type = 'UI'
670     bl_label = 'Object / Material Indices'
671     bl_options = {'DEFAULT_CLOSED'}
672
673     @classmethod
674     def poll(cls, context):
675         node = context.active_node
676         return node and node.type == 'ID_MASK'
677
678     def draw(self, context):
679         layout = self.layout
680
681         objects = bpy.data.objects
682         materials = bpy.data.materials
683         node = context.active_node
684
685         show_ob_id = False
686         show_ma_id = False
687         matching_ids = False
688
689         if context.active_object:
690             ob_act = context.active_object
691         else:
692             ob_act = False
693
694         for ob in objects:
695             if ob and ob.pass_index > 0:
696                 show_ob_id = True
697         for ma in materials:
698             if ma and ma.pass_index > 0:
699                 show_ma_id = True
700         row = layout.row(align=True)  
701         row.prop(node, 'index', text="Mask Index")
702         row.prop(node, 'use_matching_indices', text="Only Matching IDs")
703         
704         layout.separator()
705
706         if not show_ob_id and not show_ma_id:
707             layout.label(text="No objects or materials indices so far.", icon="INFO")
708
709         if show_ob_id:
710             split = layout.split()
711             col = split.column()
712             col.label(text="Object Name")
713             split.label(text="ID Number")
714             row = layout.row()
715             for ob in objects:
716                 icon = "OUTLINER_DATA_" + ob.type
717                 if ob.library:
718                     icon = "LIBRARY_DATA_DIRECT"
719                 elif ob.is_library_indirect:
720                     icon = "LIBRARY_DATA_INDIRECT"
721
722                 if ob and node.use_matching_indices \
723                       and ob.pass_index == node.index \
724                       and ob.pass_index != 0:
725                     matching_ids = True
726                     row.label(
727                       text="[{}]".format(ob.name)
728                           if ob_act and ob.name == ob_act.name else ob.name,
729                       icon=icon)
730                     row.label(text="%s" % ob.pass_index)
731                     row = layout.row()
732
733                 elif ob and not node.use_matching_indices \
734                         and ob.pass_index > 0:
735
736                     matching_ids = True
737                     row.label(
738                       text="[{}]".format(ob.name)
739                           if ob_act and ob.name == ob_act.name else ob.name,
740                       icon=icon)
741                     row.label(text="%s" % ob.pass_index)
742                     row = layout.row()
743
744             if node.use_matching_indices and not matching_ids:
745                 row.label(text="No objects with ID %s" % node.index, icon="INFO")
746
747             layout.separator()
748
749         if show_ma_id:
750             split = layout.split()
751             col = split.column()
752             col.label(text="Material Name")
753             split.label(text="ID Number")
754             row = layout.row()
755
756             for ma in materials:
757                 icon = "BLANK1"
758                 if ma.use_nodes:
759                     icon = "NODETREE"
760                 elif ma.library:
761                     icon = "LIBRARY_DATA_DIRECT"
762                     if ma.is_library_indirect:
763                         icon = "LIBRARY_DATA_INDIRECT"
764
765                 if ma and node.use_matching_indices \
766                       and ma.pass_index == node.index \
767                       and ma.pass_index != 0:
768                     matching_ids = True
769                     row.label(text="%s" % ma.name, icon=icon)
770                     row.label(text="%s" % ma.pass_index)
771                     row = layout.row()
772
773                 elif ma and not node.use_matching_indices \
774                         and ma.pass_index > 0:
775
776                     matching_ids = True
777                     row.label(text="%s" % ma.name, icon=icon)
778                     row.label(text="%s" % ma.pass_index)
779                     row = layout.row()
780
781             if node.use_matching_indices and not matching_ids:
782                 row.label(text="No materials with ID %s" % node.index, icon="INFO")
783
784
785 # // FEATURE: OB/MA ID panel in Node Editor
786
787 # FEATURE: Unsimplify on render
788 @persistent
789 def unsimplify_render_pre(scene):
790     render = scene.render
791     scene.simplify_status = render.use_simplify
792
793     if scene.use_unsimplify_render:
794         render.use_simplify = False
795
796 @persistent
797 def unsimplify_render_post(scene):
798     render = scene.render
799     render.use_simplify = scene.simplify_status
800
801 def unsimplify_ui(self,context):
802     scene = bpy.context.scene
803     self.layout.prop(scene, 'use_unsimplify_render')
804 # //FEATURE: Unsimplify on render
805
806 # FEATURE: Extra Info Stats
807 def stats_scene(self, context):
808
809     preferences = context.user_preferences.addons[__name__].preferences
810
811     if preferences.use_scene_stats:
812         scenes_count = str(len(bpy.data.scenes))
813         cameras_count = str(len(bpy.data.cameras))
814         cameras_selected = 0
815         meshlights = 0
816         meshlights_visible = 0
817
818         for ob in context.scene.objects:
819             if cycles_is_emission(context, ob):
820                 meshlights += 1
821                 if ob in context.visible_objects:
822                     meshlights_visible += 1
823
824             if ob in context.selected_objects:
825                 if ob.type == 'CAMERA':
826                     cameras_selected += 1
827     
828         meshlights_string = '| Meshlights:{}/{}'.format(meshlights_visible, meshlights)
829     
830         row = self.layout.row(align=True)
831         row.label(text="Scenes:{} | Cameras:{}/{} {}".format(
832                    scenes_count, cameras_selected, cameras_count,
833                    meshlights_string if context.scene.render.engine == 'CYCLES' else ''))
834
835 # //FEATURE: Extra Info Stats
836
837 # FEATURE: Camera Bounds as Render Border
838 class AMTH_VIEW3D_OT_render_border_camera(Operator):
839     """Set camera bounds as render border"""
840     bl_idname = "view3d.render_border_camera"
841     bl_label = "Camera as Render Border"
842
843     @classmethod
844     def poll(cls, context):
845         return context.space_data.region_3d.view_perspective == 'CAMERA'
846
847     def execute(self, context):
848         render = context.scene.render
849         render.use_border = True
850         render.border_min_x = 0
851         render.border_min_y = 0
852         render.border_max_x = 1
853         render.border_max_y = 1
854
855         return {'FINISHED'}
856
857 def button_render_border_camera(self, context):
858
859     view3d = context.space_data.region_3d
860     
861     if view3d.view_perspective == 'CAMERA':
862         layout = self.layout
863         layout.separator()
864         layout.operator(AMTH_VIEW3D_OT_render_border_camera.bl_idname,
865                         text="Camera as Render Border", icon="FULLSCREEN_ENTER")
866
867 # //FEATURE: Camera Bounds as Render Border
868
869 # FEATURE: Passepartout options on W menu
870 def button_camera_passepartout(self, context):
871
872     view3d = context.space_data.region_3d
873     cam = context.scene.camera.data
874     
875     if view3d.view_perspective == 'CAMERA':
876         layout = self.layout
877         if cam.show_passepartout:
878             layout.prop(cam, "passepartout_alpha", text="Passepartout")
879         else:
880             layout.prop(cam, "show_passepartout")
881
882 # FEATURE: Show Only Render with Alt+Shift+Z
883 class AMTH_VIEW3D_OT_show_only_render(Operator):
884     bl_idname = "view3d.show_only_render"
885     bl_label = "Show Only Render"
886
887     def execute(self, context):
888         space = bpy.context.space_data
889         
890         if space.show_only_render:
891             space.show_only_render = False
892         else:
893             space.show_only_render = True
894         return {'FINISHED'}
895
896
897 # FEATURE: Display Active Image Node on Image Editor
898 # Made by Sergey Sharybin, tweaks from Bassam Kurdali
899 image_nodes = {"CompositorNodeImage",
900                "ShaderNodeTexImage",
901                "ShaderNodeTexEnvironment"}
902
903 class AMTH_NODE_OT_show_active_node_image(Operator):
904     """Show active image node image in the image editor"""
905     bl_idname = "node.show_active_node_image"
906     bl_label = "Show Active Node Node"
907     bl_options = {'UNDO'}
908
909     def execute(self, context):
910         preferences = context.user_preferences.addons[__name__].preferences
911         if preferences.use_image_node_display:
912             if context.active_node:
913                 active_node = context.active_node
914                 if active_node.bl_idname in image_nodes and active_node.image:
915                     for area in context.screen.areas:
916                         if area.type == "IMAGE_EDITOR":
917                             for space in area.spaces:
918                                 if space.type == "IMAGE_EDITOR":
919                                     space.image = active_node.image
920                             break
921     
922         return {'FINISHED'}
923 # // FEATURE: Display Active Image Node on Image Editor
924
925 # FEATURE: Select Meshlights
926 class AMTH_OBJECT_OT_select_meshlights(Operator):
927     """Select light emitting meshes"""
928     bl_idname = "object.select_meshlights"
929     bl_label = "Select Meshlights"
930     bl_options = {'UNDO'}
931
932     @classmethod
933     def poll(cls, context):
934         return context.scene.render.engine == 'CYCLES'
935
936     def execute(self, context):
937         # Deselect everything first
938         bpy.ops.object.select_all(action='DESELECT')
939
940         for ob in context.scene.objects:
941             if cycles_is_emission(context, ob):
942                 ob.select = True
943                 context.scene.objects.active = ob
944
945         if not context.selected_objects and not context.scene.objects.active:
946             self.report({'INFO'}, "No meshlights to select")
947
948         return {'FINISHED'}
949
950 def button_select_meshlights(self, context):
951     
952     if context.scene.render.engine == 'CYCLES':
953         self.layout.operator('object.select_meshlights', icon="LAMP_SUN")
954 # // FEATURE: Select Meshlights
955
956 # FEATURE: Mesh Symmetry Tools by Sergey Sharybin
957 class AMTH_MESH_OT_find_asymmetric(Operator):
958     """
959     Find asymmetric vertices
960     """
961
962     bl_idname = "mesh.find_asymmetric"
963     bl_label = "Find Asymmetric"
964     bl_options = {'UNDO', 'REGISTER'}
965
966     @classmethod
967     def poll(cls, context):
968         object = context.object
969         if object:
970             return object.mode == 'EDIT' and object.type == 'MESH'
971         return False
972
973     def execute(self, context):
974         threshold = 1e-6
975
976         object = context.object
977         bm = bmesh.from_edit_mesh(object.data)
978
979         # Deselect all the vertices
980         for v in bm.verts:
981             v.select = False
982
983         for v1 in bm.verts:
984             if abs(v1.co[0]) < threshold:
985                 continue
986
987             mirror_found = False
988             for v2 in bm.verts:
989                 if v1 == v2:
990                     continue
991                 if v1.co[0] * v2.co[0] > 0.0:
992                     continue
993
994                 mirror_coord = Vector(v2.co)
995                 mirror_coord[0] *= -1
996                 if (mirror_coord - v1.co).length_squared < threshold:
997                     mirror_found = True
998                     break
999             if not mirror_found:
1000                 v1.select = True
1001
1002         bm.select_flush_mode()
1003
1004         bmesh.update_edit_mesh(object.data)
1005
1006         return {'FINISHED'}
1007
1008 class AMTH_MESH_OT_make_symmetric(Operator):
1009     """
1010     Make symmetric
1011     """
1012
1013     bl_idname = "mesh.make_symmetric"
1014     bl_label = "Make Symmetric"
1015     bl_options = {'UNDO', 'REGISTER'}
1016
1017     @classmethod
1018     def poll(cls, context):
1019         object = context.object
1020         if object:
1021             return object.mode == 'EDIT' and object.type == 'MESH'
1022         return False
1023
1024     def execute(self, context):
1025         threshold = 1e-6
1026
1027         object = context.object
1028         bm = bmesh.from_edit_mesh(object.data)
1029
1030         for v1 in bm.verts:
1031             if v1.co[0] < threshold:
1032                 continue
1033             if not v1.select:
1034                 continue
1035
1036             closest_vert = None
1037             closest_distance = -1
1038             for v2 in bm.verts:
1039                 if v1 == v2:
1040                     continue
1041                 if v2.co[0] > threshold:
1042                     continue
1043                 if not v2.select:
1044                     continue
1045
1046                 mirror_coord = Vector(v2.co)
1047                 mirror_coord[0] *= -1
1048                 distance = (mirror_coord - v1.co).length_squared
1049                 if closest_vert is None or distance < closest_distance:
1050                     closest_distance = distance
1051                     closest_vert = v2
1052
1053             if closest_vert:
1054                 closest_vert.select = False
1055                 closest_vert.co = Vector(v1.co)
1056                 closest_vert.co[0] *= -1
1057             v1.select = False
1058
1059         for v1 in bm.verts:
1060             if v1.select:
1061                 closest_vert = None
1062                 closest_distance = -1
1063                 for v2 in bm.verts:
1064                     if v1 != v2:
1065                         mirror_coord = Vector(v2.co)
1066                         mirror_coord[0] *= -1
1067                         distance = (mirror_coord - v1.co).length_squared
1068                         if closest_vert is None or distance < closest_distance:
1069                             closest_distance = distance
1070                             closest_vert = v2
1071                 if closest_vert:
1072                     v1.select = False
1073                     v1.co = Vector(closest_vert.co)
1074                     v1.co[0] *= -1
1075
1076         bm.select_flush_mode()
1077         bmesh.update_edit_mesh(object.data)
1078
1079         return {'FINISHED'}
1080 # // FEATURE: Mesh Symmetry Tools by Sergey Sharybin
1081
1082 # FEATURE: Cycles Render Sampling Extra
1083 def render_cycles_scene_samples(self, context):
1084
1085     layout = self.layout
1086
1087     scenes = bpy.data.scenes
1088     scene = context.scene
1089     cscene = scene.cycles
1090     render = scene.render
1091     list_sampling = scene.amaranth_cycles_list_sampling
1092
1093     if cscene.progressive == 'BRANCHED_PATH':
1094         layout.separator()
1095         split = layout.split()
1096         col = split.column()
1097
1098         col.operator(
1099             AMTH_RENDER_OT_cycles_samples_percentage_set.bl_idname,
1100             text="%s" % 'Set as Render Samples' if cscene.use_samples_final else 'Set New Render Samples',
1101             icon="%s" % 'PINNED' if cscene.use_samples_final else 'UNPINNED')
1102
1103         col = split.column()
1104         row = col.row(align=True)
1105         row.enabled = True if scene.get('amth_cycles_samples_final') else False
1106
1107         row.operator(
1108             AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
1109             text="100%").percent=100
1110         row.operator(
1111             AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
1112             text="75%").percent=75
1113         row.operator(
1114             AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
1115             text="50%").percent=50
1116         row.operator(
1117             AMTH_RENDER_OT_cycles_samples_percentage.bl_idname,
1118             text="25%").percent=25
1119
1120     # List Lamps
1121     if (len(scene.render.layers) > 1) or \
1122         (len(bpy.data.scenes) > 1):
1123
1124         box = layout.box()
1125         row = box.row(align=True)
1126         col = row.column(align=True)
1127
1128         row = col.row(align=True)
1129         row.alignment = 'LEFT'
1130         row.prop(scene, 'amaranth_cycles_list_sampling',
1131                     icon="%s" % 'TRIA_DOWN' if list_sampling else 'TRIA_RIGHT',
1132                     emboss=False)
1133
1134     if list_sampling:
1135         if len(scene.render.layers) == 1 and \
1136             render.layers[0].samples == 0:
1137             pass
1138         else:
1139             col.separator()
1140             col.label(text="RenderLayers:", icon='RENDERLAYERS')
1141
1142             for rl in scene.render.layers:
1143                 row = col.row(align=True)
1144                 row.label(rl.name, icon='BLANK1')
1145                 row.prop(rl, "samples", text="%s" %
1146                     "Samples" if rl.samples > 0 else "Automatic (%s)" % (
1147                         cscene.aa_samples if cscene.progressive == 'BRANCHED_PATH' else cscene.samples))
1148
1149         if (len(bpy.data.scenes) > 1):
1150             col.separator()
1151
1152             col.label(text="Scenes:", icon='SCENE_DATA')
1153             row = col.row(align=True)
1154
1155             if cscene.progressive == 'PATH':
1156                 for s in bpy.data.scenes:
1157                     if s != scene:
1158                         row = col.row(align=True)
1159                         if s.render.engine == 'CYCLES':
1160                             cscene = s.cycles
1161
1162                             row.label(s.name)
1163                             row.prop(cscene, "samples", icon='BLANK1')
1164                         else:
1165                             row.label(text="Scene: '%s' is not using Cycles" % s.name)
1166             else:
1167                 for s in bpy.data.scenes:
1168                     if s != scene:
1169                         row = col.row(align=True)
1170                         if s.render.engine == 'CYCLES':
1171                             cscene = s.cycles
1172
1173                             row.label(s.name, icon='BLANK1')
1174                             row.prop(cscene, "aa_samples",
1175                                 text="AA Samples")
1176                         else:
1177                             row.label(text="Scene: '%s' is not using Cycles" % s.name)
1178
1179 # // FEATURE: Cycles Render Sampling Extra
1180
1181 # FEATURE: Motion Paths Extras
1182 class AMTH_POSE_OT_paths_clear_all(Operator):
1183     """Clear motion paths from all bones"""
1184     bl_idname = "pose.paths_clear_all"
1185     bl_label = "Clear All Motion Paths"
1186     bl_options = {'UNDO'}
1187
1188     @classmethod
1189     def poll(cls, context):
1190         return context.mode == 'POSE'
1191
1192     def execute(self, context):
1193         #silly but works
1194         for b in context.object.data.bones:
1195             b.select = True
1196             bpy.ops.pose.paths_clear()
1197             b.select = False
1198         return {'FINISHED'}
1199
1200 class AMTH_POSE_OT_paths_frame_match(Operator):
1201     """Match Start/End frame of scene to motion path range"""
1202     bl_idname = "pose.paths_frame_match"
1203     bl_label = "Match Frame Range"
1204     bl_options = {'UNDO'}
1205
1206     def execute(self, context):
1207         avs = context.object.pose.animation_visualization
1208         scene = context.scene
1209
1210         if avs.motion_path.type == 'RANGE':
1211             if scene.use_preview_range:
1212                 avs.motion_path.frame_start = scene.frame_preview_start
1213                 avs.motion_path.frame_end = scene.frame_preview_end
1214             else:
1215                 avs.motion_path.frame_start = scene.frame_start
1216                 avs.motion_path.frame_end = scene.frame_end
1217
1218         else:
1219             if scene.use_preview_range:
1220                 avs.motion_path.frame_before = scene.frame_preview_start
1221                 avs.motion_path.frame_after = scene.frame_preview_end
1222             else:
1223                 avs.motion_path.frame_before = scene.frame_start
1224                 avs.motion_path.frame_after = scene.frame_end
1225
1226         return {'FINISHED'}
1227
1228 def pose_motion_paths_ui(self, context):
1229
1230     layout = self.layout
1231     scene = context.scene
1232     avs = context.object.pose.animation_visualization
1233     if context.active_pose_bone:
1234         mpath = context.active_pose_bone.motion_path
1235     layout.separator()    
1236     layout.label(text="Motion Paths Extras:")
1237
1238     split = layout.split()
1239
1240     col = split.column(align=True)
1241
1242     if context.selected_pose_bones:
1243         if mpath:
1244             sub = col.row(align=True)
1245             sub.operator("pose.paths_update", text="Update Path", icon='BONE_DATA')
1246             sub.operator("pose.paths_clear", text="", icon='X')
1247         else:
1248             col.operator("pose.paths_calculate", text="Calculate Path", icon='BONE_DATA')
1249     else:
1250         col.label(text="Select Bones First", icon="ERROR")
1251
1252     col = split.column(align=True)
1253     col.operator(AMTH_POSE_OT_paths_frame_match.bl_idname,
1254         text="{}".format( "Set Preview Frame Range"
1255                 if scene.use_preview_range else "Set Frame Range"),
1256         icon="{}".format("PREVIEW_RANGE"
1257                 if scene.use_preview_range else "TIME"))
1258
1259     col = layout.column()
1260     row = col.row(align=True)
1261
1262     if avs.motion_path.type == 'RANGE':
1263         row.prop(avs.motion_path, "frame_start", text="Start")
1264         row.prop(avs.motion_path, "frame_end", text="End")
1265     else:
1266         row.prop(avs.motion_path, "frame_before", text="Before")
1267         row.prop(avs.motion_path, "frame_after", text="After")
1268
1269     layout.separator()
1270     layout.operator(AMTH_POSE_OT_paths_clear_all.bl_idname, icon="X")
1271 # // FEATURE: Motion Paths Extras
1272
1273 # FEATURE: Final Render Resolution Display
1274 def render_final_resolution_ui(self, context):
1275
1276     rd = context.scene.render
1277     layout = self.layout
1278
1279     final_res_x = (rd.resolution_x * rd.resolution_percentage) / 100
1280     final_res_y = (rd.resolution_y * rd.resolution_percentage) / 100
1281
1282     if rd.use_border:
1283        final_res_x_border = round((final_res_x * (rd.border_max_x - rd.border_min_x)))
1284        final_res_y_border = round((final_res_y * (rd.border_max_y - rd.border_min_y)))
1285        layout.label(text="Final Resolution: {} x {} [Border: {} x {}]".format(
1286              str(final_res_x)[:-2], str(final_res_y)[:-2],
1287              str(final_res_x_border), str(final_res_y_border)))
1288     else:
1289         layout.label(text="Final Resolution: {} x {}".format(
1290              str(final_res_x)[:-2], str(final_res_y)[:-2]))
1291 # // FEATURE: Final Render Resolution Display
1292
1293 # FEATURE: Shader Nodes Extra Info
1294 def node_shader_extra(self, context):
1295
1296     if context.space_data.tree_type == 'ShaderNodeTree':
1297         ob = context.active_object
1298         snode = context.space_data
1299         layout = self.layout
1300
1301         if ob and snode.shader_type != 'WORLD':
1302             if ob.type == 'LAMP':
1303                 layout.label(text="%s" % ob.name,
1304                              icon="LAMP_%s" % ob.data.type)        
1305             else:
1306                 layout.label(text="%s" % ob.name,
1307                              icon="OUTLINER_DATA_%s" % ob.type)
1308              
1309
1310 # // FEATURE: Shader Nodes Extra Info
1311
1312 # FEATURE: Scene Debug
1313 class AMTH_SCENE_OT_cycles_shader_list_nodes(Operator):
1314     """List Cycles materials containing a specific shader"""
1315     bl_idname = "scene.cycles_list_nodes"
1316     bl_label = "List Materials"
1317     materials = []
1318
1319     @classmethod
1320     def poll(cls, context):
1321         return context.scene.render.engine == 'CYCLES'
1322
1323     def execute(self, context):
1324         node_type = context.scene.amaranth_cycles_node_types
1325         roughness = False
1326         self.__class__.materials = []
1327         shaders_roughness = ['BSDF_GLOSSY','BSDF_DIFFUSE','BSDF_GLASS']
1328
1329         print("\n=== Cycles Shader Type: %s === \n" % node_type)
1330
1331         for ma in bpy.data.materials:
1332             if ma.node_tree:
1333                 nodes = ma.node_tree.nodes
1334                 
1335                 print_unconnected = ('Note: \nOutput from "%s" node' % node_type,
1336                                         'in material "%s"' % ma.name, 'not connected\n')
1337
1338                 for no in nodes:
1339                     if no.type == node_type:
1340                         for ou in no.outputs:
1341                             if ou.links:
1342                                 connected = True
1343                                 if no.type in shaders_roughness:
1344                                     roughness = 'R: %.4f' % no.inputs['Roughness'].default_value
1345                                 else:
1346                                     roughness = False
1347                             else:
1348                                 connected = False
1349                                 print(print_unconnected)
1350
1351                             if ma.name not in self.__class__.materials:
1352                                 self.__class__.materials.append('%s%s [%s] %s%s%s' % (
1353                                     '[L] ' if ma.library else '',
1354                                     ma.name, ma.users,
1355                                     '[F]' if ma.use_fake_user else '',
1356                                     ' - [%s]' % roughness if roughness else '',
1357                                     ' * Output not connected' if not connected else ''))
1358
1359                     elif no.type == 'GROUP':
1360                         if no.node_tree:
1361                             for nog in no.node_tree.nodes:
1362                                 if nog.type == node_type:
1363                                     for ou in nog.outputs:
1364                                         if ou.links:
1365                                             connected = True
1366                                             if nog.type in shaders_roughness:
1367                                                 roughness = 'R: %.4f' % nog.inputs['Roughness'].default_value
1368                                             else:
1369                                                 roughness = False
1370                                         else:
1371                                             connected = False
1372                                             print(print_unconnected)
1373
1374                                         if ma.name not in self.__class__.materials:
1375                                             self.__class__.materials.append('%s%s%s [%s] %s%s%s' % (
1376                                                 '[L] ' if ma.library else '',
1377                                                 'Node Group:  %s%s  ->  ' % (
1378                                                     '[L] ' if no.node_tree.library else '',
1379                                                     no.node_tree.name),
1380                                                 ma.name, ma.users,
1381                                                 '[F]' if ma.use_fake_user else '',
1382                                                 ' - [%s]' % roughness if roughness else '',
1383                                                 ' * Output not connected' if not connected else ''))
1384
1385                     self.__class__.materials = sorted(list(set(self.__class__.materials)))
1386
1387         if len(self.__class__.materials) == 0:
1388             self.report({"INFO"}, "No materials with nodes type %s found" % node_type)
1389         else:
1390             print("* A total of %d %s using %s was found \n" % (
1391                     len(self.__class__.materials),
1392                     "material" if len(self.__class__.materials) == 1 else "materials",
1393                     node_type))
1394
1395             count = 0
1396
1397             for mat in self.__class__.materials:
1398                 print('%02d. %s' % (count+1, self.__class__.materials[count]))
1399                 count += 1
1400             print("\n")
1401
1402         self.__class__.materials = sorted(list(set(self.__class__.materials)))
1403
1404         return {'FINISHED'}
1405
1406 class AMTH_SCENE_OT_cycles_shader_list_nodes_clear(Operator):
1407     """Clear the list below"""
1408     bl_idname = "scene.cycles_list_nodes_clear"
1409     bl_label = "Clear Materials List"
1410     
1411     def execute(self, context):
1412         AMTH_SCENE_OT_cycles_shader_list_nodes.materials[:] = []
1413         print("* Cleared Cycles Materials List")
1414         return {'FINISHED'}
1415
1416 class AMTH_SCENE_OT_amaranth_object_select(Operator):
1417     '''Select object'''
1418     bl_idname = "scene.amaranth_object_select"
1419     bl_label = "Select Object"
1420     object = bpy.props.StringProperty()
1421  
1422     def execute(self, context):
1423         if self.object:
1424             object = bpy.data.objects[self.object]
1425
1426             bpy.ops.object.select_all(action='DESELECT')
1427             object.select = True
1428             context.scene.objects.active = object
1429
1430         return{'FINISHED'}
1431
1432 class AMTH_SCENE_OT_list_missing_node_links(Operator):
1433     '''Print a list of missing node links'''
1434     bl_idname = "scene.list_missing_node_links"
1435     bl_label = "List Missing Node Links"
1436
1437     count_groups = 0
1438     count_images = 0
1439     count_image_node_unlinked = 0
1440
1441     def execute(self, context):
1442         missing_groups = []
1443         missing_images = []
1444         image_nodes_unlinked = []
1445         libraries = []
1446         self.__class__.count_groups = 0
1447         self.__class__.count_images = 0
1448         self.__class__.count_image_node_unlinked = 0
1449
1450         for ma in bpy.data.materials:
1451             if ma.node_tree:
1452                 for no in ma.node_tree.nodes:
1453                     if no.type == 'GROUP':
1454                         if not no.node_tree:
1455                             self.__class__.count_groups += 1
1456
1457                             users_ngroup = []
1458
1459                             for ob in bpy.data.objects:
1460                                 if ob.material_slots and ma.name in ob.material_slots:
1461                                     users_ngroup.append("%s%s%s" % (
1462                                         "[L] " if ob.library else "",
1463                                         "[F] " if ob.use_fake_user else "",
1464                                         ob.name))
1465
1466                             missing_groups.append("MA: %s%s%s [%s]%s%s%s\n" % (
1467                                 "[L] " if ma.library else "",
1468                                 "[F] " if ma.use_fake_user else "",
1469                                 ma.name, ma.users,
1470                                 " *** No users *** " if ma.users == 0 else "",
1471                                 "\nLI: %s" % 
1472                                 ma.library.filepath if ma.library else "",
1473                                 "\nOB: %s" % ',  '.join(users_ngroup) if users_ngroup else ""))
1474
1475                             if ma.library:
1476                                 libraries.append(ma.library.filepath)
1477                     if no.type == 'TEX_IMAGE':
1478
1479                         outputs_empty = not no.outputs['Color'].is_linked and not no.outputs['Alpha'].is_linked
1480
1481                         if no.image:
1482                             import os.path
1483                             image_path_exists = os.path.exists(
1484                                                     bpy.path.abspath(
1485                                                         no.image.filepath, library=no.image.library))
1486
1487                         if outputs_empty or not \
1488                            no.image or not \
1489                            image_path_exists:
1490
1491                             users_images = []
1492
1493                             for ob in bpy.data.objects:
1494                                 if ob.material_slots and ma.name in ob.material_slots:
1495                                     users_images.append("%s%s%s" % (
1496                                         "[L] " if ob.library else "",
1497                                         "[F] " if ob.use_fake_user else "",
1498                                         ob.name))
1499
1500                             if outputs_empty:
1501                                 self.__class__.count_image_node_unlinked += 1
1502
1503                                 image_nodes_unlinked.append("%s%s%s%s%s [%s]%s%s%s%s%s\n" % (
1504                                     "NO: %s" % no.name,
1505                                     "\nMA: ",
1506                                     "[L] " if ma.library else "",
1507                                     "[F] " if ma.use_fake_user else "",
1508                                     ma.name, ma.users,
1509                                     " *** No users *** " if ma.users == 0 else "",
1510                                     "\nLI: %s" % 
1511                                     ma.library.filepath if ma.library else "",
1512                                     "\nIM: %s" % no.image.name if no.image else "",
1513                                     "\nLI: %s" % no.image.filepath if no.image and no.image.filepath else "",
1514                                     "\nOB: %s" % ',  '.join(users_images) if users_images else ""))
1515                             
1516
1517                             if not no.image or not image_path_exists:
1518                                 self.__class__.count_images += 1
1519
1520                                 missing_images.append("MA: %s%s%s [%s]%s%s%s%s%s\n" % (
1521                                     "[L] " if ma.library else "",
1522                                     "[F] " if ma.use_fake_user else "",
1523                                     ma.name, ma.users,
1524                                     " *** No users *** " if ma.users == 0 else "",
1525                                     "\nLI: %s" % 
1526                                     ma.library.filepath if ma.library else "",
1527                                     "\nIM: %s" % no.image.name if no.image else "",
1528                                     "\nLI: %s" % no.image.filepath if no.image and no.image.filepath else "",
1529                                     "\nOB: %s" % ',  '.join(users_images) if users_images else ""))
1530
1531                                 if ma.library:
1532                                     libraries.append(ma.library.filepath)
1533
1534         # Remove duplicates and sort
1535         missing_groups = sorted(list(set(missing_groups)))
1536         missing_images = sorted(list(set(missing_images)))
1537         image_nodes_unlinked = sorted(list(set(image_nodes_unlinked)))
1538         libraries = sorted(list(set(libraries)))
1539
1540         print("\n\n== %s missing image %s, %s missing node %s and %s image %s unlinked ==" %
1541             ("No" if self.__class__.count_images == 0 else str(self.__class__.count_images),
1542             "node" if self.__class__.count_images == 1 else "nodes",
1543             "no" if self.__class__.count_groups == 0 else str(self.__class__.count_groups),
1544             "group" if self.__class__.count_groups == 1 else "groups",
1545             "no" if self.__class__.count_image_node_unlinked == 0 else str(self.__class__.count_image_node_unlinked),
1546             "node" if self.__class__.count_groups == 1 else "nodes"))
1547
1548         # List Missing Node Groups
1549         if missing_groups:
1550             print("\n* Missing Node Group Links\n")
1551             for mig in missing_groups:
1552                 print(mig)
1553
1554         # List Missing Image Nodes
1555         if missing_images:
1556             print("\n* Missing Image Nodes Link\n")
1557
1558             for mii in missing_images:
1559                 print(mii)
1560
1561         # List Image Nodes with its outputs unlinked
1562         if image_nodes_unlinked:
1563             print("\n* Image Nodes Unlinked\n")
1564
1565             for nou in image_nodes_unlinked:
1566                 print(nou)
1567
1568         if missing_groups or \
1569            missing_images or \
1570            image_nodes_unlinked:
1571             if libraries:
1572                 print("\nThat's bad, run check on %s:" % (
1573                     "this library" if len(libraries) == 1 else "these libraries"))
1574                 for li in libraries:
1575                     print(li)
1576         else:
1577             self.report({"INFO"}, "Yay! No missing node links")            
1578
1579         print("\n")
1580
1581         if missing_groups and missing_images:
1582             self.report({"WARNING"}, "%d missing image %s and %d missing node %s found" %
1583                 (self.__class__.count_images, "node" if self.__class__.count_images == 1 else "nodes",
1584                 self.__class__.count_groups, "group" if self.__class__.count_groups == 1 else "groups"))
1585
1586         return{'FINISHED'}
1587
1588 class AMTH_SCENE_OT_list_missing_material_slots(Operator):
1589     '''List objects with empty material slots'''
1590     bl_idname = "scene.list_missing_material_slots"
1591     bl_label = "List Empty Material Slots"
1592
1593     objects = []
1594     libraries = []
1595
1596     def execute(self, context):
1597         self.__class__.objects = []
1598         self.__class__.libraries = []
1599
1600         for ob in bpy.data.objects:
1601             for ma in ob.material_slots:
1602                 if not ma.material:
1603                     self.__class__.objects.append('%s%s' % (
1604                         '[L] ' if ob.library else '',
1605                         ob.name))
1606                     if ob.library:
1607                         self.__class__.libraries.append(ob.library.filepath)
1608
1609         self.__class__.objects = sorted(list(set(self.__class__.objects)))
1610         self.__class__.libraries = sorted(list(set(self.__class__.libraries)))
1611
1612         if len(self.__class__.objects) == 0:
1613             self.report({"INFO"}, "No objects with empty material slots found")
1614         else:
1615             print("\n* A total of %d %s with empty material slots was found \n" % (
1616                     len(self.__class__.objects),
1617                     "object" if len(self.__class__.objects) == 1 else "objects"))
1618
1619             count = 0
1620             count_lib = 0
1621
1622             for obs in self.__class__.objects:
1623                 print('%02d. %s' % (
1624                     count+1, self.__class__.objects[count]))
1625                 count += 1
1626
1627             if self.__class__.libraries:
1628                 print("\n\n* Check %s:\n" % 
1629                     ("this library" if len(self.__class__.libraries) == 1
1630                         else "these libraries"))
1631
1632                 for libs in self.__class__.libraries:
1633                     print('%02d. %s' % (
1634                         count_lib+1, self.__class__.libraries[count_lib]))
1635                     count_lib += 1
1636             print("\n")
1637
1638         return{'FINISHED'}
1639
1640 class AMTH_SCENE_OT_list_missing_material_slots_clear(Operator):
1641     """Clear the list below"""
1642     bl_idname = "scene.list_missing_material_slots_clear"
1643     bl_label = "Clear Empty Material Slots List"
1644     
1645     def execute(self, context):
1646         AMTH_SCENE_OT_list_missing_material_slots.objects[:] = []
1647         print("* Cleared Empty Material Slots List")
1648         return {'FINISHED'}
1649
1650 class AMTH_SCENE_OT_blender_instance_open(Operator):
1651     '''Open in a new Blender instance'''
1652     bl_idname = "scene.blender_instance_open"
1653     bl_label = "Open Blender Instance"
1654     filepath = bpy.props.StringProperty()
1655
1656     def execute(self, context):
1657         if self.filepath:
1658             import os.path
1659             filepath = os.path.normpath(bpy.path.abspath(self.filepath))
1660
1661             import subprocess
1662             try:
1663                 subprocess.Popen([bpy.app.binary_path, filepath])
1664             except:
1665                 print("Error on the new Blender instance")
1666                 import traceback
1667                 traceback.print_exc()
1668
1669         return{'FINISHED'}
1670
1671 class AMTH_SCENE_PT_scene_debug(Panel):
1672     '''Scene Debug'''
1673     bl_label = 'Scene Debug'
1674     bl_space_type = "PROPERTIES"
1675     bl_region_type = "WINDOW"
1676     bl_context = "scene"
1677
1678     def draw_header(self, context):
1679         layout = self.layout
1680         layout.label(text="", icon="RADIO")
1681
1682     def draw(self, context):
1683         layout = self.layout
1684         scene = context.scene
1685         objects =  bpy.data.objects
1686         ob_act = context.active_object
1687         images = bpy.data.images
1688         lamps = bpy.data.lamps
1689         images_missing = []
1690         list_missing_images = scene.amaranth_debug_scene_list_missing_images
1691         materials = AMTH_SCENE_OT_cycles_shader_list_nodes.materials
1692         materials_count = len(AMTH_SCENE_OT_cycles_shader_list_nodes.materials)
1693         missing_material_slots_obs = AMTH_SCENE_OT_list_missing_material_slots.objects
1694         missing_material_slots_count = len(AMTH_SCENE_OT_list_missing_material_slots.objects)
1695         missing_material_slots_lib = AMTH_SCENE_OT_list_missing_material_slots.libraries
1696         engine = scene.render.engine
1697
1698         # List Missing Images
1699         box = layout.box()
1700         row = box.row(align=True)
1701         split = row.split()
1702         col = split.column()
1703
1704         if images:
1705             import os.path
1706
1707             for im in images:
1708                 if im.type not in ['UV_TEST', 'RENDER_RESULT', 'COMPOSITING']: 
1709                     if not os.path.exists(bpy.path.abspath(im.filepath, library=im.library)):
1710                         images_missing.append(["%s%s [%s]%s" % (
1711                             '[L] ' if im.library else '',
1712                             im.name, im.users,
1713                             ' [F]' if im.use_fake_user else ''),
1714                             im.filepath if im.filepath else 'No Filepath',
1715                             im.library.filepath if im.library else ''])
1716
1717             if images_missing:
1718                 row = col.row(align=True)
1719                 row.alignment = 'LEFT'
1720                 row.prop(scene, 'amaranth_debug_scene_list_missing_images',
1721                             icon="%s" % 'TRIA_DOWN' if list_missing_images else 'TRIA_RIGHT',
1722                             emboss=False)
1723
1724                 split = split.split()
1725                 col = split.column()
1726
1727                 col.label(text="%s missing %s" % (
1728                              str(len(images_missing)),
1729                              'image' if len(images_missing) == 1 else 'images'),
1730                              icon="ERROR")
1731
1732                 if list_missing_images:
1733                     col = box.column(align=True)
1734                     for mis in images_missing:
1735                         col.label(text=mis[0],
1736                          icon="IMAGE_DATA")
1737                         col.label(text=mis[1], icon="LIBRARY_DATA_DIRECT")
1738                         if mis[2]:
1739                             row = col.row(align=True)
1740                             row.alignment = "LEFT"
1741                             row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname,
1742                                          text=mis[2],
1743                                          icon="LINK_BLEND",
1744                                          emboss=False).filepath=mis[2]
1745                         col.separator()
1746             else:
1747                 row = col.row(align=True)
1748                 row.alignment = 'LEFT'
1749                 row.label(text="Great! No missing images", icon="RIGHTARROW_THIN")
1750
1751                 split = split.split()
1752                 col = split.column()
1753
1754                 col.label(text="%s %s loading correctly" % (
1755                              str(len(images)),
1756                              'image' if len(images) == 1 else 'images'),
1757                              icon="IMAGE_DATA")
1758         else:
1759             row = col.row(align=True)
1760             row.alignment = 'LEFT'
1761             row.label(text="No images loaded yet", icon="RIGHTARROW_THIN")
1762
1763         # List Cycles Materials by Shader
1764         if engine == 'CYCLES':
1765             box = layout.box()
1766             split = box.split()
1767             col = split.column(align=True)
1768             col.prop(scene, 'amaranth_cycles_node_types',
1769                 icon="MATERIAL")
1770
1771             row = split.row(align=True)
1772             row.operator(AMTH_SCENE_OT_cycles_shader_list_nodes.bl_idname,
1773                             icon="SORTSIZE",
1774                             text="List Materials Using Shader")
1775             if materials_count != 0: 
1776                 row.operator(AMTH_SCENE_OT_cycles_shader_list_nodes_clear.bl_idname,
1777                                 icon="X", text="")
1778             col.separator()
1779
1780             try:
1781                 materials
1782             except NameError:
1783                 pass
1784             else:
1785                 if materials_count != 0: 
1786                     col = box.column(align=True)
1787                     count = 0
1788                     col.label(text="%s %s found" % (materials_count,
1789                         'material' if materials_count == 1 else 'materials'), icon="INFO")
1790                     for mat in materials:
1791                         count += 1
1792                         col.label(text='%s' % (materials[count-1]), icon="MATERIAL")
1793
1794         # List Missing Node Trees
1795         box = layout.box()
1796         row = box.row(align=True)
1797         split = row.split()
1798         col = split.column(align=True)
1799
1800         split = col.split()
1801         split.label(text="Node Links")
1802         split.operator(AMTH_SCENE_OT_list_missing_node_links.bl_idname,
1803                         icon="NODETREE")
1804
1805         if AMTH_SCENE_OT_list_missing_node_links.count_groups != 0 or \
1806             AMTH_SCENE_OT_list_missing_node_links.count_images != 0 or \
1807             AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked != 0:
1808             col.label(text="Warning! Check Console", icon="ERROR")
1809
1810         if AMTH_SCENE_OT_list_missing_node_links.count_groups != 0:
1811             col.label(text="%s" % ("%s node %s missing link" % (
1812                      str(AMTH_SCENE_OT_list_missing_node_links.count_groups),
1813                      "group" if AMTH_SCENE_OT_list_missing_node_links.count_groups == 1 else "groups")),
1814                      icon="NODETREE")
1815         if AMTH_SCENE_OT_list_missing_node_links.count_images != 0:
1816             col.label(text="%s" % ("%s image %s missing link" % (
1817                      str(AMTH_SCENE_OT_list_missing_node_links.count_images),
1818                      "node" if AMTH_SCENE_OT_list_missing_node_links.count_images == 1 else "nodes")),
1819                      icon="IMAGE_DATA")
1820
1821         if AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked != 0:
1822             col.label(text="%s" % ("%s image %s with no output conected" % (
1823                      str(AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked),
1824                      "node" if AMTH_SCENE_OT_list_missing_node_links.count_image_node_unlinked == 1 else "nodes")),
1825                      icon="NODE")
1826
1827         # List Empty Materials Slots
1828         box = layout.box()
1829         split = box.split()
1830         col = split.column(align=True)
1831         col.label(text="Material Slots")
1832
1833         row = split.row(align=True)
1834         row.operator(AMTH_SCENE_OT_list_missing_material_slots.bl_idname,
1835                         icon="MATERIAL",
1836                         text="List Empty Materials Slots")
1837         if missing_material_slots_count != 0: 
1838             row.operator(AMTH_SCENE_OT_list_missing_material_slots_clear.bl_idname,
1839                             icon="X", text="")
1840         col.separator()
1841
1842         try:
1843             missing_material_slots_obs
1844         except NameError:
1845             pass
1846         else:
1847             if missing_material_slots_count != 0: 
1848                 col = box.column(align=True)
1849                 count = 0
1850                 count_lib = 0
1851                 col.label(text="%s %s with empty material slots found" % (
1852                     missing_material_slots_count,
1853                     'object' if missing_material_slots_count == 1 else 'objects'),
1854                     icon="INFO")
1855
1856                 for obs in missing_material_slots_obs:
1857                     count += 1
1858
1859                     row = col.row()
1860                     row.alignment = 'LEFT'
1861                     row.label(text='%s' % missing_material_slots_obs[count-1],
1862                                 icon="OBJECT_DATA")
1863
1864                 if missing_material_slots_lib:
1865                     col.separator()
1866                     col.label("Check %s:" % (
1867                         "this library" if
1868                             len(missing_material_slots_lib) == 1
1869                                 else "these libraries"))
1870                     
1871                     for libs in missing_material_slots_lib:
1872                         count_lib += 1
1873                         row = col.row(align=True)
1874                         row.alignment = "LEFT"
1875                         row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname,
1876                                      text=missing_material_slots_lib[count_lib-1],
1877                                      icon="LINK_BLEND",
1878                                      emboss=False).filepath=missing_material_slots_lib[count_lib-1]
1879
1880 # // FEATURE: Scene Debug
1881 # FEATURE: Dupli  Group Path
1882 def ui_dupli_group_library_path(self, context):
1883
1884     ob = context.object
1885
1886     row = self.layout.row()
1887     row.alignment = 'LEFT'
1888
1889     if ob and ob.dupli_group and ob.dupli_group.library:
1890         lib = ob.dupli_group.library.filepath
1891
1892         row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname,
1893             text="Library: %s" % lib,
1894             emboss=False,
1895             icon="LINK_BLEND").filepath=lib
1896
1897 # // FEATURE: Dupli  Group Path
1898 # FEATURE: Color Management Presets
1899 class AMTH_SCENE_MT_color_management_presets(Menu):
1900     """List of Color Management presets"""
1901     bl_label = "Color Management Presets"
1902     preset_subdir = "color"
1903     preset_operator = "script.execute_preset"
1904     draw = Menu.draw_preset
1905
1906
1907 class AMTH_AddPresetColorManagement(AddPresetBase, Operator):
1908     """Add or remove a Color Management preset"""
1909     bl_idname = "scene.color_management_preset_add"
1910     bl_label = "Add Color Management Preset"
1911     preset_menu = "AMTH_SCENE_MT_color_management_presets"
1912
1913     preset_defines = [
1914         "scene = bpy.context.scene"
1915     ]
1916
1917     preset_values = [
1918         "scene.view_settings.view_transform",
1919         "scene.display_settings.display_device",
1920         "scene.view_settings.exposure",
1921         "scene.view_settings.gamma",
1922         "scene.view_settings.look",
1923         "scene.view_settings.use_curve_mapping",
1924         "scene.sequencer_colorspace_settings.name",
1925     ]
1926
1927     preset_subdir = "color"
1928
1929 def ui_color_management_presets(self, context):
1930     
1931     layout = self.layout
1932
1933     row = layout.row(align=True)
1934     row.menu("AMTH_SCENE_MT_color_management_presets", text=bpy.types.AMTH_SCENE_MT_color_management_presets.bl_label)
1935     row.operator("scene.color_management_preset_add", text="", icon="ZOOMIN")
1936     row.operator("scene.color_management_preset_add", text="", icon="ZOOMOUT").remove_active = True
1937     layout.separator()
1938 # // FEATURE: Color Management Presets
1939
1940 # FEATURE: Sequencer Extra Info
1941 def act_strip(context):
1942     try:
1943         return context.scene.sequence_editor.active_strip
1944     except AttributeError:
1945         return None
1946
1947 def ui_sequencer_extra_info(self, context):
1948
1949     layout = self.layout
1950     strip = act_strip(context)
1951
1952     if strip:
1953         seq_type = strip.type
1954
1955         if seq_type and seq_type == 'IMAGE':
1956             elem = strip.strip_elem_from_frame(context.scene.frame_current)
1957             if elem:
1958                 layout.label(text="%s %s" % (
1959                     elem.filename,
1960                     "[%s]" % (context.scene.frame_current - strip.frame_start)))
1961 # // FEATURE: Sequencer Extra Info
1962
1963 # FEATURE: Normal Node Values, by Lukas Tönne
1964 def normal_vector_get(self):
1965     return self.outputs['Normal'].default_value
1966
1967 def normal_vector_set(self, values):
1968     # default_value allows un-normalized values,
1969     # do this here to prevent awkward results
1970     values = Vector(values).normalized()
1971     self.outputs['Normal'].default_value = values
1972
1973 prop_normal_vector = bpy.props.FloatVectorProperty(
1974                         name="Normal", size=3, subtype='XYZ',
1975                         min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0,
1976                         get=normal_vector_get, set=normal_vector_set
1977                         )
1978
1979 def act_node(context):
1980     try:
1981         return context.active_node
1982     except AttributeError:
1983         return None
1984
1985 def ui_node_normal_values(self, context):
1986
1987     node = act_node(context)
1988
1989     if act_node:
1990         if node and node.type == 'NORMAL':
1991             self.layout.prop(node, "normal_vector", text="")
1992
1993 # // FEATURE: Normal Node Values, by Lukas Tönne
1994
1995 # FEATURE: Object ID for objects inside DupliGroups
1996 class AMTH_OBJECT_OT_id_dupligroup(Operator):
1997     '''Set the Object ID for objects in the dupli group'''
1998     bl_idname = "object.amaranth_object_id_duplis"
1999     bl_label = "Apply Object ID to Duplis"
2000
2001     clear = False
2002
2003     @classmethod
2004     def poll(cls, context):
2005         return context.active_object.dupli_group
2006
2007     def execute(self, context):
2008         self.__class__.clear = False
2009         ob = context.active_object
2010         amth_text_exists = amaranth_text_startup(context)
2011         script_exists = False
2012         script_intro = "# OB ID: %s" % ob.name
2013         obdata = "bpy.data.objects['%s']" % ob.name
2014         script = "%s" % (
2015             "\nif %(obdata)s and %(obdata)s.dupli_group and %(obdata)s.pass_index != 0: %(obname)s \n"
2016             "    for dob in %(obdata)s.dupli_group.objects: %(obname)s \n"
2017             "        dob.pass_index = %(obdata)s.pass_index %(obname)s \n" %
2018                 {'obdata' : obdata, 'obname' : script_intro})
2019
2020         for txt in bpy.data.texts:
2021             if txt.name == amth_text.name:
2022                 for li in txt.lines:
2023                     if script_intro == li.body:
2024                         script_exists = True
2025                         continue
2026
2027         if not script_exists:
2028             amth_text.write("\n")
2029             amth_text.write(script_intro)
2030             amth_text.write(script)
2031
2032         if ob and ob.dupli_group:
2033             if ob.pass_index != 0:
2034                 for dob in ob.dupli_group.objects:
2035                     dob.pass_index = ob.pass_index
2036
2037         self.report({'INFO'},
2038             "%s ID: %s to all objects in this Dupli Group" % (
2039                 "Applied" if not script_exists else "Updated",
2040                 ob.pass_index))
2041
2042         return{'FINISHED'}
2043
2044 class AMTH_OBJECT_OT_id_dupligroup_clear(Operator):
2045     '''Clear the Object ID from objects in dupli group'''
2046     bl_idname = "object.amaranth_object_id_duplis_clear"
2047     bl_label = "Clear Object ID from Duplis"
2048
2049     @classmethod
2050     def poll(cls, context):
2051         return context.active_object.dupli_group
2052
2053     def execute(self, context):
2054         context.active_object.pass_index = 0
2055         AMTH_OBJECT_OT_id_dupligroup.clear = True
2056         amth_text_exists = amaranth_text_startup(context)
2057         match_first = "# OB ID: %s" % context.active_object.name
2058
2059         if amth_text_exists:
2060             for txt in bpy.data.texts:
2061                 if txt.name == amth_text.name:
2062                     for li in txt.lines:
2063                         if match_first in li.body:
2064                             li.body = ''
2065                             continue
2066
2067         self.report({'INFO'}, "Object IDs back to normal")
2068         return{'FINISHED'}
2069
2070 def ui_object_id_duplis(self, context):
2071
2072     if context.active_object.dupli_group:
2073         split = self.layout.split()
2074         row = split.row(align=True)
2075         row.enabled = context.active_object.pass_index != 0
2076         row.operator(
2077             AMTH_OBJECT_OT_id_dupligroup.bl_idname)
2078         row.operator(
2079             AMTH_OBJECT_OT_id_dupligroup_clear.bl_idname,
2080             icon="X", text="")
2081         split.separator()
2082
2083         if AMTH_OBJECT_OT_id_dupligroup.clear:
2084             self.layout.label(text="Next time you save/reload this file, "
2085                                         "object IDs will be back to normal",
2086                               icon="INFO")
2087
2088 # // FEATURE: Object ID for objects inside DupliGroups
2089 # UI: Warning about Z not connected when using EXR
2090 def ui_render_output_z(self, context):
2091
2092     scene = bpy.context.scene
2093     image = scene.render.image_settings
2094     if scene.render.use_compositing and \
2095         image.file_format == 'OPEN_EXR' and \
2096         image.use_zbuffer:
2097         if scene.node_tree and scene.node_tree.nodes:
2098             for no in scene.node_tree.nodes:
2099                 if no.type == 'COMPOSITE':
2100                     if not no.inputs['Z'].is_linked:
2101                         self.layout.label(
2102                             text="The Z output in node \"%s\" is not connected" % 
2103                                 no.name, icon="ERROR")
2104
2105 # // UI: Warning about Z not connected
2106
2107 # FEATURE: Delete Materials not assigned to any verts
2108 class AMTH_OBJECT_OT_material_remove_unassigned(Operator):
2109     '''Remove materials not assigned to any vertex'''
2110     bl_idname = "object.amaranth_object_material_remove_unassigned"
2111     bl_label = "Remove Unassigned Materials"
2112
2113     @classmethod
2114     def poll(cls, context):
2115         return context.active_object.material_slots
2116
2117     def execute(self, context):
2118
2119         act_ob = context.active_object
2120         count = len(act_ob.material_slots)
2121         materials_removed = []
2122         act_ob.active_material_index = 0
2123
2124         for slot in act_ob.material_slots:
2125             count -= 1
2126
2127             bpy.ops.object.mode_set(mode='EDIT')
2128             bpy.ops.mesh.select_all(action='DESELECT')
2129             act_ob.active_material_index = count
2130             bpy.ops.object.material_slot_select()
2131             
2132             if act_ob.data.total_vert_sel == 0 or \
2133                 (len(act_ob.material_slots) == 1 and not \
2134                     act_ob.material_slots[0].material):
2135                 materials_removed.append(
2136                     "%s" % act_ob.active_material.name if act_ob.active_material else "Empty")
2137                 bpy.ops.object.mode_set(mode='OBJECT')
2138                 bpy.ops.object.material_slot_remove()
2139             else:
2140                 pass
2141
2142         bpy.ops.object.mode_set(mode='EDIT')
2143         bpy.ops.mesh.select_all(action='DESELECT')
2144         bpy.ops.object.mode_set(mode='OBJECT')
2145
2146         if materials_removed:
2147             print("\n* Removed %s Unassigned Materials \n" % len(materials_removed))
2148
2149             count_mr = 0
2150
2151             for mr in materials_removed:
2152                 count_mr += 1
2153                 print("%0.2d. %s" % (count_mr, materials_removed[count_mr - 1]))
2154
2155             print("\n")
2156             self.report({'INFO'}, "Removed %s Unassigned Materials" %
2157                 len(materials_removed))
2158
2159         return{'FINISHED'}
2160
2161 def ui_material_remove_unassigned(self, context):
2162
2163     self.layout.operator(
2164         AMTH_OBJECT_OT_material_remove_unassigned.bl_idname,
2165         icon="X")
2166
2167 # // FEATURE: Delete Materials not assigned to any verts
2168 # FEATURE: Cycles Samples Percentage
2169 class AMTH_RENDER_OT_cycles_samples_percentage_set(Operator):
2170     '''Save the current number of samples per shader as final (gets saved in .blend)'''
2171     bl_idname = "scene.amaranth_cycles_samples_percentage_set"
2172     bl_label = "Set as Render Samples"
2173
2174     def execute(self, context):
2175         cycles = context.scene.cycles
2176         cycles.use_samples_final = True
2177
2178         context.scene['amth_cycles_samples_final'] = [
2179             cycles.diffuse_samples,
2180             cycles.glossy_samples,
2181             cycles.transmission_samples,
2182             cycles.ao_samples,
2183             cycles.mesh_light_samples,
2184             cycles.subsurface_samples,
2185             cycles.volume_samples]
2186
2187         self.report({'INFO'}, "Render Samples Saved")
2188
2189         return{'FINISHED'}
2190
2191
2192 class AMTH_RENDER_OT_cycles_samples_percentage(Operator):
2193     '''Set a percentage of the final render samples'''
2194     bl_idname = "scene.amaranth_cycles_samples_percentage"
2195     bl_label = "Set Render Samples Percentage"
2196
2197     percent = IntProperty(
2198                 name="Percentage",
2199                 description="Percentage to divide render samples by",
2200                 subtype='PERCENTAGE',
2201                 default=0)
2202
2203     def execute(self, context):
2204         percent = self.percent
2205         cycles = context.scene.cycles
2206         cycles_samples_final = context.scene['amth_cycles_samples_final']
2207
2208         cycles.use_samples_final = False
2209
2210         if percent == 100:
2211             cycles.use_samples_final = True
2212
2213         cycles.diffuse_samples = int((cycles_samples_final[0] / 100) * percent)
2214         cycles.glossy_samples = int((cycles_samples_final[1] / 100) * percent)
2215         cycles.transmission_samples = int((cycles_samples_final[2] / 100) * percent)
2216         cycles.ao_samples = int((cycles_samples_final[3] / 100) * percent)
2217         cycles.mesh_light_samples = int((cycles_samples_final[4] / 100) * percent)
2218         cycles.subsurface_samples = int((cycles_samples_final[5] / 100) * percent)
2219         cycles.volume_samples = int((cycles_samples_final[6] / 100) * percent)
2220
2221         return{'FINISHED'}
2222
2223 # //FEATURE: Cycles Samples Percentage
2224 # FEATURE: Jump forward/backward every N frames
2225 class AMTH_SCREEN_OT_frame_jump(Operator):
2226     '''Jump a number of frames forward/backwards'''
2227     bl_idname = "screen.amaranth_frame_jump"
2228     bl_label = "Jump Frames"
2229
2230     forward = BoolProperty(default=True)
2231
2232     def execute(self, context):
2233         scene = context.scene
2234         preferences = context.user_preferences.addons[__name__].preferences
2235
2236         if self.forward:
2237             scene.frame_current = scene.frame_current + preferences.frames_jump
2238         else:
2239             scene.frame_current = scene.frame_current - preferences.frames_jump
2240
2241         return{'FINISHED'}
2242
2243 def ui_userpreferences_edit(self, context):
2244     preferences = context.user_preferences.addons[__name__].preferences
2245
2246     col = self.layout.column()
2247     split = col.split(percentage=0.21)
2248     split.prop(preferences, "frames_jump",
2249                text="Frames to Jump")
2250
2251 # // FEATURE: Jump forward/backward every N frames
2252 # FEATURE: Set Layers to Render
2253 class AMTH_SCENE_OT_layers_render_save(Operator):
2254     '''Save the current scene layers as those that should be enabled for final renders'''
2255     bl_idname = "scene.amaranth_layers_render_save"
2256     bl_label = "Save as Layers for Render"
2257
2258     def execute(self, context):
2259         which = []
2260         n = -1
2261
2262         for l in context.scene.layers:
2263             n += 1
2264             if l:
2265                 which.append(n)
2266
2267         context.scene['amth_layers_for_render'] = which
2268         self.report({'INFO'}, "Layers for Render Saved")
2269
2270         return{'FINISHED'}
2271
2272 class AMTH_SCENE_OT_layers_render_view(Operator):
2273     '''Enable the scene layers that should be active for final renders'''
2274     bl_idname = "scene.amaranth_layers_render_view"
2275     bl_label = "View Layers for Render"
2276
2277     def execute(self, context):
2278         scene = context.scene
2279         layers_render = scene['amth_layers_for_render']
2280
2281         for window in bpy.context.window_manager.windows:
2282             screen = window.screen
2283
2284             for area in screen.areas:
2285                 if area.type == 'VIEW_3D':
2286                     override = {'window': window, 'screen': screen, 'scene': scene, 
2287                                 'area': area, 'region': area.regions[4],
2288                                 'blend_data': context.blend_data}
2289
2290                     if layers_render:
2291                         bpy.ops.view3d.layers(override, nr=layers_render[0]+1, extend=False, toggle=False)
2292
2293                         for n in layers_render:
2294                             context.scene.layers[n] = True
2295                     else:
2296                         bpy.ops.view3d.layers(override, nr=1, extend=False, toggle=False)
2297                         self.report({'INFO'}, "No layers set for render")
2298
2299                     break
2300
2301         return{'FINISHED'}
2302
2303 class AMTH_SCENE_OT_layers_render_set_individual(Operator):
2304     '''Whether this layer should be enabled or not for final renders'''
2305     bl_idname = "scene.amaranth_layers_render_set_individual"
2306     bl_label = "Set This Layer for Render"
2307
2308     toggle = BoolProperty()
2309     number = IntProperty()
2310
2311     def execute(self, context):
2312         toggle = self.toggle
2313         number = self.number
2314
2315         new_layers = []
2316
2317         for la in context.scene['amth_layers_for_render']:
2318             new_layers.append(la)
2319
2320         if len(context.scene['amth_layers_for_render']) and number in new_layers:
2321             new_layers.remove(number)
2322         else:
2323             new_layers.append(number)
2324
2325         # Remove Duplicates
2326         new_layers = list(set(new_layers))
2327         context.scene['amth_layers_for_render'] = new_layers
2328
2329         bpy.ops.scene.amaranth_layers_render_view()
2330
2331         return{'FINISHED'}
2332
2333 class AMTH_SCENE_OT_layers_render_clear(Operator):
2334     '''Clear layers for render'''
2335     bl_idname = "scene.amaranth_layers_render_clear"
2336     bl_label = "Clear Layers for Render"
2337
2338     def execute(self, context):
2339
2340         if context.scene.get('amth_layers_for_render'):
2341             context.scene['amth_layers_for_render'] = []
2342
2343         return{'FINISHED'}
2344
2345 def ui_layers_for_render(self, context):
2346
2347     lfr_available = context.scene.get('amth_layers_for_render')
2348     if lfr_available:
2349         lfr = context.scene['amth_layers_for_render']
2350
2351     layout = self.layout
2352     layout.label("Layers for Rendering:")
2353     split = layout.split()
2354     col = split.column(align=True)
2355     row = col.row(align=True)
2356     row.operator(
2357         AMTH_SCENE_OT_layers_render_save.bl_idname,
2358         text="Replace Layers" if lfr_available else "Save Current Layers for Render",
2359         icon="FILE_REFRESH" if lfr_available else 'LAYER_USED')
2360
2361     if lfr_available:
2362         row.operator(
2363             AMTH_SCENE_OT_layers_render_clear.bl_idname,
2364             icon='X', text="")
2365         col = col.column(align=True)
2366         col.enabled = True if lfr_available else False
2367         col.operator(
2368             AMTH_SCENE_OT_layers_render_view.bl_idname,
2369             icon="RESTRICT_VIEW_OFF")
2370
2371         split = split.split()
2372         col = split.column(align=True)
2373         row = col.row(align=True)
2374
2375         for n in range(0,5):
2376             row.operator(
2377                 AMTH_SCENE_OT_layers_render_set_individual.bl_idname, text="",
2378                 icon='LAYER_ACTIVE' if n in lfr else 'BLANK1').number = n
2379         row = col.row(align=True)
2380         for n in range(10,15):
2381             row.operator(
2382                 AMTH_SCENE_OT_layers_render_set_individual.bl_idname, text="",
2383                 icon='LAYER_ACTIVE' if n in lfr else 'BLANK1').number = n
2384
2385         split = split.split()
2386         col = split.column(align=True)
2387         row = col.row(align=True)
2388
2389         for n in range(5,10):
2390             row.operator(
2391                 AMTH_SCENE_OT_layers_render_set_individual.bl_idname, text="",
2392                 icon='LAYER_ACTIVE' if n in lfr else 'BLANK1').number = n
2393         row = col.row(align=True)
2394         for n in range(15,20):
2395             row.operator(
2396                 AMTH_SCENE_OT_layers_render_set_individual.bl_idname, text="",
2397                 icon='LAYER_ACTIVE' if n in lfr else 'BLANK1').number = n
2398
2399 def ui_layers_for_render_header(self, context):
2400     if context.scene.get('amth_layers_for_render'):
2401         self.layout.operator(
2402             AMTH_SCENE_OT_layers_render_view.bl_idname,
2403             text="", icon="IMGDISPLAY")
2404
2405 # // FEATURE: Set Layers to Render
2406 # FEATURE: Lighters Corner
2407 class AMTH_LightersCorner(bpy.types.Panel):
2408     """The Lighters Panel"""
2409     bl_label = "Lighter's Corner"
2410     bl_idname = "AMTH_SCENE_PT_lighters_corner"
2411     bl_space_type = 'PROPERTIES'
2412     bl_region_type = 'WINDOW'
2413     bl_context = "scene"
2414
2415     @classmethod
2416     def poll(cls, context):
2417         any_lamps = False
2418         for ob in bpy.data.objects:
2419             if ob.type == 'LAMP' or cycles_is_emission(context, ob):
2420                 any_lamps = True
2421             else:
2422                 pass
2423         return any_lamps
2424
2425     def draw_header(self, context):
2426         layout = self.layout
2427         layout.label(text="", icon="LAMP_SUN")
2428
2429     def draw(self, context):
2430         layout = self.layout
2431         scene = context.scene
2432         objects =  bpy.data.objects
2433         ob_act = context.active_object
2434         lamps = bpy.data.lamps
2435         list_meshlights = scene.amaranth_lighterscorner_list_meshlights
2436         engine = scene.render.engine
2437
2438         layout.prop(scene, "amaranth_lighterscorner_list_meshlights")
2439
2440         box = layout.box()
2441         if lamps:
2442             if objects:
2443                 row = box.row(align=True)
2444                 split = row.split(percentage=0.42)
2445                 col = split.column()
2446                 col.label(text="Name")
2447
2448                 split = split.split(percentage=0.1)
2449                 col = split.column()
2450                 col.label(text="", icon="BLANK1")
2451                 if engine in ['CYCLES', 'BLENDER_RENDER']:
2452                     if engine == 'BLENDER_RENDER':
2453                         split = split.split(percentage=0.7)
2454                     else:
2455                         split = split.split(percentage=0.35)
2456                     col = split.column()
2457                     col.label(text="Samples")
2458
2459                 if engine == 'CYCLES':
2460                     split = split.split(percentage=0.35)
2461                     col = split.column()
2462                     col.label(text="Size")
2463
2464                 split = split.split(percentage=0.8)
2465                 col = split.column()
2466                 col.label(text="Visibility")
2467
2468                 for ob in objects:
2469                     is_lamp = ob.type == 'LAMP'
2470                     is_emission = True if cycles_is_emission(context, ob) and list_meshlights else False
2471
2472                     if ob and is_lamp or is_emission:
2473                         lamp = ob.data
2474                         clamp = ob.data.cycles
2475
2476                         row = box.row(align=True)
2477                         split = row.split(percentage=0.5)
2478                         col = split.column()
2479                         row = col.row()
2480                         row.alignment = 'LEFT'
2481                         row.operator(AMTH_SCENE_OT_amaranth_object_select.bl_idname,
2482                                     text='%s %s%s' % (
2483                                         " [L] " if ob.library else "",
2484                                         ob.name,
2485                                         "" if ob.name in context.scene.objects else " [Not in Scene]"),
2486                                     icon="%s" % ('LAMP_%s' % ob.data.type if is_lamp else 'MESH_GRID'),
2487                                     emboss=False).object = ob.name
2488                         if ob.library:
2489                             row = col.row(align=True)
2490                             row.alignment = "LEFT"
2491                             row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname,
2492                                          text=ob.library.filepath,
2493                                          icon="LINK_BLEND",
2494                                          emboss=False).filepath=ob.library.filepath
2495
2496                         if engine == 'CYCLES':
2497                             split = split.split(percentage=0.35)
2498                             col = split.column()
2499                             if is_lamp:
2500                                 if scene.cycles.progressive == 'BRANCHED_PATH':
2501                                     col.prop(clamp, "samples", text="")
2502                                 if scene.cycles.progressive == 'PATH':
2503                                    col.label(text="N/A")
2504                             else:
2505                               col.label(text="N/A")
2506
2507                         if engine == 'BLENDER_RENDER':
2508                             split = split.split(percentage=0.7)
2509                             col = split.column()
2510                             if is_lamp:
2511                                 if lamp.type == 'HEMI':
2512                                     col.label(text="Not Available")
2513                                 elif lamp.type == 'AREA' and lamp.shadow_method == 'RAY_SHADOW':
2514                                     row = col.row(align=True)
2515                                     row.prop(lamp, "shadow_ray_samples_x", text="X")
2516                                     if lamp.shape == 'RECTANGLE':
2517                                         row.prop(lamp, "shadow_ray_samples_y", text="Y")
2518                                 elif lamp.shadow_method == 'RAY_SHADOW':
2519                                     col.prop(lamp, "shadow_ray_samples", text="Ray Samples")
2520                                 elif lamp.shadow_method == 'BUFFER_SHADOW':
2521                                     col.prop(lamp, "shadow_buffer_samples", text="Buffer Samples")
2522                                 else:
2523                                     col.label(text="No Shadow")
2524                             else:
2525                               col.label(text="N/A")
2526
2527                         if engine == 'CYCLES':
2528                             split = split.split(percentage=0.4)
2529                             col = split.column()
2530                             if is_lamp:
2531                                 if lamp.type in ['POINT','SUN', 'SPOT']:
2532                                     col.label(text="%.2f" % lamp.shadow_soft_size)
2533                                 elif lamp.type == 'HEMI':
2534                                     col.label(text="N/A")
2535                                 elif lamp.type == 'AREA' and lamp.shape == 'RECTANGLE':
2536                                     col.label(text="%.2fx%.2f" % (lamp.size, lamp.size_y))
2537                                 else:
2538                                     col.label(text="%.2f" % lamp.size)
2539                             else:
2540                               col.label(text="N/A")
2541
2542                         split = split.split(percentage=0.8)
2543                         col = split.column()
2544                         row = col.row(align=True)
2545                         row.prop(ob, "hide", text="", emboss=False)
2546                         row.prop(ob, "hide_render", text="", emboss=False)
2547
2548                         split = split.split(percentage=0.3)
2549                         col = split.column()
2550                         col.label(text="", icon="%s" % "TRIA_LEFT" if ob == ob_act else "BLANK1")
2551         else:
2552             box.label(text="No Lamps", icon="LAMP_DATA")
2553
2554
2555 classes = (AMTH_SCENE_MT_color_management_presets,
2556            AMTH_AddPresetColorManagement,
2557            AMTH_LightersCorner,
2558            AMTH_SCENE_PT_scene_debug,
2559            AMTH_SCENE_OT_refresh,
2560            AMTH_SCENE_OT_cycles_shader_list_nodes,
2561            AMTH_SCENE_OT_cycles_shader_list_nodes_clear,
2562            AMTH_SCENE_OT_amaranth_object_select,
2563            AMTH_SCENE_OT_list_missing_node_links,
2564            AMTH_SCENE_OT_list_missing_material_slots,
2565            AMTH_SCENE_OT_list_missing_material_slots_clear,
2566            AMTH_SCENE_OT_blender_instance_open,
2567            AMTH_SCENE_OT_layers_render_save,
2568            AMTH_SCENE_OT_layers_render_view,
2569            AMTH_SCENE_OT_layers_render_set_individual,
2570            AMTH_SCENE_OT_layers_render_clear,
2571            AMTH_WM_OT_save_reload,
2572            AMTH_MESH_OT_find_asymmetric,
2573            AMTH_MESH_OT_make_symmetric,
2574            AMTH_NODE_OT_AddTemplateVignette,
2575            AMTH_NODE_MT_amaranth_templates,
2576            AMTH_FILE_OT_directory_current_blend,
2577            AMTH_FILE_OT_directory_go_to,
2578            AMTH_NODE_PT_indices,
2579            AMTH_NODE_PT_simplify,
2580            AMTH_NODE_OT_toggle_mute,
2581            AMTH_NODE_OT_show_active_node_image,
2582            AMTH_VIEW3D_OT_render_border_camera,
2583            AMTH_VIEW3D_OT_show_only_render,
2584            AMTH_OBJECT_OT_select_meshlights,
2585            AMTH_OBJECT_OT_id_dupligroup,
2586            AMTH_OBJECT_OT_id_dupligroup_clear,
2587            AMTH_OBJECT_OT_material_remove_unassigned,
2588            AMTH_POSE_OT_paths_clear_all,
2589            AMTH_POSE_OT_paths_frame_match,
2590            AMTH_RENDER_OT_cycles_samples_percentage,
2591            AMTH_RENDER_OT_cycles_samples_percentage_set,
2592            AMTH_FILE_PT_libraries,
2593            AMTH_SCREEN_OT_frame_jump)
2594
2595 addon_keymaps = []
2596
2597 def register():
2598
2599     bpy.utils.register_class(AmaranthToolsetPreferences)
2600
2601     # UI: Register the panel
2602     init_properties()
2603     for c in classes:
2604         bpy.utils.register_class(c)
2605
2606     bpy.types.VIEW3D_MT_object_specials.append(button_refresh)
2607     bpy.types.VIEW3D_MT_object_specials.append(button_render_border_camera)
2608     bpy.types.VIEW3D_MT_object_specials.append(button_camera_passepartout)
2609     bpy.types.VIEW3D_MT_object_specials.append(button_frame_current)
2610     bpy.types.VIEW3D_MT_pose_specials.append(button_frame_current)
2611     bpy.types.VIEW3D_MT_select_object.append(button_select_meshlights)
2612     bpy.types.VIEW3D_HT_header.append(ui_layers_for_render_header)
2613
2614     bpy.types.INFO_MT_file.append(button_save_reload)
2615     bpy.types.INFO_HT_header.append(stats_scene)
2616
2617     bpy.types.TIME_HT_header.append(label_timeline_extra_info)
2618
2619     bpy.types.NODE_HT_header.append(node_templates_pulldown)
2620     bpy.types.NODE_HT_header.append(node_stats)
2621     bpy.types.NODE_HT_header.append(node_shader_extra)
2622     bpy.types.NODE_PT_active_node_properties.append(ui_node_normal_values)
2623
2624     bpy.types.CyclesRender_PT_sampling.append(render_cycles_scene_samples)
2625
2626     bpy.types.FILEBROWSER_HT_header.append(button_directory_current_blend)
2627
2628     bpy.types.SCENE_PT_simplify.append(unsimplify_ui)
2629     bpy.types.CyclesScene_PT_simplify.append(unsimplify_ui)
2630
2631     bpy.types.DATA_PT_display.append(pose_motion_paths_ui)
2632
2633     bpy.types.RENDER_PT_dimensions.append(render_final_resolution_ui)
2634     bpy.types.RENDER_PT_output.append(ui_render_output_z)
2635
2636     bpy.types.SCENE_PT_color_management.prepend(ui_color_management_presets)
2637
2638     bpy.types.SEQUENCER_HT_header.append(ui_sequencer_extra_info)
2639
2640     bpy.types.OBJECT_PT_duplication.append(ui_dupli_group_library_path)
2641
2642     bpy.types.OBJECT_PT_relations.append(ui_object_id_duplis)
2643
2644     bpy.types.MATERIAL_MT_specials.append(ui_material_remove_unassigned)
2645
2646     bpy.types.USERPREF_PT_edit.append(ui_userpreferences_edit)
2647
2648     bpy.types.RENDERLAYER_PT_layers.append(ui_layers_for_render)
2649
2650     bpy.app.handlers.render_pre.append(unsimplify_render_pre)
2651     bpy.app.handlers.render_post.append(unsimplify_render_post)
2652
2653     wm = bpy.context.window_manager
2654     kc = wm.keyconfigs.addon
2655     if kc:
2656         km = kc.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
2657         km.keymap_items.new("node.show_active_node_image", 'ACTIONMOUSE', 'RELEASE')
2658         km.keymap_items.new("node.show_active_node_image", 'SELECTMOUSE', 'RELEASE')
2659
2660         km = kc.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
2661         kmi = km.keymap_items.new('wm.call_menu', 'W', 'PRESS')
2662         kmi.properties.name = "AMTH_NODE_MT_amaranth_templates"
2663
2664         km = kc.keymaps.new(name='Window')
2665         kmi = km.keymap_items.new('scene.refresh', 'F5', 'PRESS', shift=False, ctrl=False)
2666         kmi = km.keymap_items.new('wm.save_reload', 'W', 'PRESS', shift=True, ctrl=True)
2667
2668         km = kc.keymaps.new(name='Frames')
2669         kmi = km.keymap_items.new('screen.amaranth_frame_jump', 'UP_ARROW', 'PRESS', shift=True)
2670         kmi.properties.forward = True
2671         kmi = km.keymap_items.new('screen.amaranth_frame_jump', 'DOWN_ARROW', 'PRESS', shift=True)
2672         kmi.properties.forward = False
2673
2674         km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
2675         kmi = km.keymap_items.new('view3d.show_only_render', 'Z', 'PRESS', shift=True, alt=True)
2676
2677         km = kc.keymaps.new(name='Graph Editor', space_type='GRAPH_EDITOR')
2678         kmi = km.keymap_items.new('wm.context_set_enum', 'TAB', 'PRESS', ctrl=True)
2679         kmi.properties.data_path = 'area.type'
2680         kmi.properties.value = 'DOPESHEET_EDITOR'
2681
2682         km = kc.keymaps.new(name='Dopesheet', space_type='DOPESHEET_EDITOR')
2683         kmi = km.keymap_items.new('wm.context_set_enum', 'TAB', 'PRESS', ctrl=True)
2684         kmi.properties.data_path = 'area.type'
2685         kmi.properties.value = 'GRAPH_EDITOR'
2686
2687         km = kc.keymaps.new(name='Dopesheet', space_type='DOPESHEET_EDITOR')
2688         kmi = km.keymap_items.new('wm.context_toggle_enum', 'TAB', 'PRESS', shift=True)
2689         kmi.properties.data_path = 'space_data.mode'
2690         kmi.properties.value_1 = 'ACTION'
2691         kmi.properties.value_2 = 'DOPESHEET'
2692
2693         addon_keymaps.append((km, kmi))
2694
2695 def unregister():
2696
2697     bpy.utils.unregister_class(AmaranthToolsetPreferences)
2698
2699     for c in classes:
2700         bpy.utils.unregister_class(c)
2701
2702     bpy.types.VIEW3D_MT_object_specials.remove(button_refresh)
2703     bpy.types.VIEW3D_MT_object_specials.remove(button_render_border_camera)
2704     bpy.types.VIEW3D_MT_object_specials.remove(button_camera_passepartout)
2705     bpy.types.VIEW3D_MT_object_specials.remove(button_frame_current)
2706     bpy.types.VIEW3D_MT_pose_specials.remove(button_frame_current)
2707     bpy.types.VIEW3D_MT_select_object.remove(button_select_meshlights)
2708     bpy.types.VIEW3D_HT_header.remove(ui_layers_for_render_header)
2709
2710     bpy.types.INFO_MT_file.remove(button_save_reload)
2711     bpy.types.INFO_HT_header.remove(stats_scene)
2712
2713     bpy.types.TIME_HT_header.remove(label_timeline_extra_info)
2714
2715     bpy.types.NODE_HT_header.remove(node_templates_pulldown)
2716     bpy.types.NODE_HT_header.remove(node_stats)
2717     bpy.types.NODE_HT_header.remove(node_shader_extra)
2718     bpy.types.NODE_PT_active_node_properties.remove(ui_node_normal_values)
2719
2720     bpy.types.CyclesRender_PT_sampling.remove(render_cycles_scene_samples)
2721
2722     bpy.types.FILEBROWSER_HT_header.remove(button_directory_current_blend)
2723
2724     bpy.types.SCENE_PT_simplify.remove(unsimplify_ui)
2725     bpy.types.CyclesScene_PT_simplify.remove(unsimplify_ui)
2726
2727     bpy.types.DATA_PT_display.remove(pose_motion_paths_ui)
2728
2729     bpy.types.RENDER_PT_dimensions.remove(render_final_resolution_ui)
2730     bpy.types.RENDER_PT_output.remove(ui_render_output_z)
2731
2732     bpy.types.SCENE_PT_color_management.remove(ui_color_management_presets)
2733
2734     bpy.types.SEQUENCER_HT_header.remove(ui_sequencer_extra_info)
2735
2736     bpy.types.OBJECT_PT_duplication.remove(ui_dupli_group_library_path)
2737
2738     bpy.types.OBJECT_PT_relations.remove(ui_object_id_duplis)
2739
2740     bpy.types.MATERIAL_MT_specials.remove(ui_material_remove_unassigned)
2741
2742     bpy.types.USERPREF_PT_edit.remove(ui_userpreferences_edit)
2743
2744     bpy.types.RENDERLAYER_PT_layers.remove(ui_layers_for_render)
2745
2746     bpy.app.handlers.render_pre.remove(unsimplify_render_pre)
2747     bpy.app.handlers.render_post.remove(unsimplify_render_post)
2748
2749     for km, kmi in addon_keymaps:
2750         km.keymap_items.remove(kmi)
2751     addon_keymaps.clear()
2752     
2753     clear_properties()
2754
2755 if __name__ == "__main__":
2756     register()