55c52c108985ac8eee2328866e6f9638854a3280
[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",
22     "version": (0, 8, 1),
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
36 from mathutils import Vector
37 from bpy.app.handlers import persistent
38 from bl_operators.presets import AddPresetBase
39
40 # Preferences
41 class AmaranthToolsetPreferences(AddonPreferences):
42     bl_idname = __name__
43     use_frame_current = BoolProperty(
44             name="Current Frame Slider",
45             description="Set the current frame from the Specials menu in the 3D View",
46             default=True,
47             )
48     use_file_save_reload = BoolProperty(
49             name="Save & Reload File",
50             description="File menu > Save & Reload, or Ctrl + Shift + W",
51             default=True,
52             )
53
54     use_scene_refresh = BoolProperty(
55             name="Refresh Scene",
56             description="Specials Menu [W], or hit F5",
57             default=True,
58             )
59     use_timeline_extra_info = BoolProperty(
60             name="Timeline Extra Info",
61             description="Timeline Header",
62             default=True,
63             )
64     use_image_node_display = BoolProperty(
65             name="Active Image Node in Editor",
66             description="Display active node image in image editor",
67             default=True,
68             )
69     use_scene_stats = BoolProperty(
70             name="Extra Scene Statistics",
71             description="Display extra scene statistics in Info editor's header",
72             default=True,
73             )
74
75
76     def draw(self, context):
77         layout = self.layout
78
79         layout.label(
80             text="Here you can enable or disable specific tools, "
81                  "in case they interfere with others or are just plain annoying")
82
83         split = layout.split(percentage=0.25)
84
85         col = split.column()
86         sub = col.column(align=True)
87         sub.label(text="3D View", icon="VIEW3D")
88         sub.prop(self, "use_frame_current")
89         sub.prop(self, "use_scene_refresh")
90
91         sub.separator()
92
93         sub.label(text="General", icon="SCENE_DATA")
94         sub.prop(self, "use_file_save_reload")
95         sub.prop(self, "use_timeline_extra_info")
96         sub.prop(self, "use_scene_stats")
97
98         sub.separator()
99
100         sub.label(text="Nodes Editor", icon="NODETREE")
101         sub.prop(self, "use_image_node_display")
102
103         col = split.column()
104         sub = col.column(align=True)
105         sub.label(text="")
106         sub.label(
107             text="Set the current frame from the Specials menu in the 3D View [W]")
108         sub.label(
109             text="Refresh the current Scene. Hotkey: F5 or in Specials menu [W]")
110
111         sub.separator()
112         sub.label(text="") # General
113         sub.label(
114             text="Quickly save and reload the current file (no warning!). "
115                  "File menu or Ctrl+Shift+W")
116         sub.label(
117             text="SMPTE Timecode and frames left/ahead on Timeline's header")
118         sub.label(
119             text="Display extra statistics for Scenes, Cameras, and Meshlights (Cycles)")
120
121         sub.separator()
122         sub.label(text="") # Nodes
123         sub.label(
124             text="When selecting an Image node, display it on the Image editor "
125                  "(if any)")
126
127 # Properties
128 def init_properties():
129
130     scene = bpy.types.Scene
131     node = bpy.types.Node
132     nodes_compo = bpy.types.CompositorNodeTree
133
134     scene.use_unsimplify_render = BoolProperty(
135         default=False,
136         name="Unsimplify Render",
137         description="Disable Simplify during render")
138     scene.simplify_status = BoolProperty(default=False)
139
140     node.use_matching_indices = BoolProperty(
141         default=True,
142         description="If disabled, display all available indices")
143
144     test_items = [
145         ("ALL", "All Types", "", 0),
146         ("BLUR", "Blur", "", 1),
147         ("BOKEHBLUR", "Bokeh Blur", "", 2),
148         ("VECBLUR", "Vector Blur", "", 3),
149         ("DEFOCUS", "Defocus", "", 4),
150         ("R_LAYERS", "Render Layer", "", 5)
151         ]
152
153     nodes_compo.types = bpy.props.EnumProperty(
154         items=test_items, name = "Types")
155
156     nodes_compo.toggle_mute = BoolProperty(default=False)
157     node.status = BoolProperty(default=False)
158
159     # Scene Debug
160     # Cycles Node Types
161     cycles_shader_node_types = [
162         ("BSDF_DIFFUSE", "Diffuse BSDF", "", 0),
163         ("BSDF_GLOSSY", "Glossy BSDF", "", 1),
164         ("BSDF_TRANSPARENT", "Transparent BSDF", "", 2),
165         ("BSDF_REFRACTION", "Refraction BSDF", "", 3),
166         ("BSDF_GLASS", "Glass BSDF", "", 4),
167         ("BSDF_TRANSLUCENT", "Translucent BSDF", "", 5),
168         ("BSDF_ANISOTROPIC", "Anisotropic BSDF", "", 6),
169         ("BSDF_VELVET", "Velvet BSDF", "", 7),
170         ("BSDF_TOON", "Toon BSDF", "", 8),
171         ("SUBSURFACE_SCATTERING", "Subsurface Scattering", "", 9),
172         ("EMISSION", "Emission", "", 10),
173         ("BSDF_HAIR", "Hair BSDF", "", 11),
174         ("BACKGROUND", "Background", "", 12),
175         ("AMBIENT_OCCLUSION", "Ambient Occlusion", "", 13),
176         ("HOLDOUT", "Holdout", "", 14),
177         ]
178
179     scene.amaranth_cycles_node_types = bpy.props.EnumProperty(
180         items=cycles_shader_node_types, name = "Shader")
181
182     scene.amaranth_debug_scene_list_lamps = BoolProperty(
183         default=False,
184         name="Lamps List",
185         description="Display a list of all the lamps")
186
187     scene.amaranth_debug_scene_list_missing_images = BoolProperty(
188         default=False,
189         name="List Missing Images",
190         description="Display a list of all the missing images")
191
192     global materials
193
194
195 def clear_properties():
196     props = (
197         "use_unsimplify_render",
198         "simplify_status",
199         "use_matching_indices",
200         "use_simplify_nodes_vector",
201         "status"
202     )
203     
204     wm = bpy.context.window_manager
205     for p in props:
206         if p in wm:
207             del wm[p]
208
209 # FEATURE: Refresh Scene!
210 class SCENE_OT_refresh(Operator):
211     """Refresh the current scene"""
212     bl_idname = "scene.refresh"
213     bl_label = "Refresh!"
214     
215     def execute(self, context):
216         preferences = context.user_preferences.addons[__name__].preferences
217         scene = context.scene
218
219         if preferences.use_scene_refresh:    
220             # Changing the frame is usually the best way to go
221             scene.frame_current = scene.frame_current
222             self.report({"INFO"}, "Scene Refreshed!")
223             
224         return {'FINISHED'}
225
226 def button_refresh(self, context):
227
228     preferences = context.user_preferences.addons[__name__].preferences
229
230     if preferences.use_scene_refresh:
231         self.layout.separator()
232         self.layout.operator(
233             SCENE_OT_refresh.bl_idname,
234             text="Refresh!",
235             icon='FILE_REFRESH')
236 # // FEATURE: Refresh Scene!
237
238 # FEATURE: Save & Reload
239 def save_reload(self, context, path):
240
241     if path:
242         bpy.ops.wm.save_mainfile()
243         self.report({'INFO'}, "Saved & Reloaded")
244         bpy.ops.wm.open_mainfile("EXEC_DEFAULT", filepath=path)
245     else:
246         bpy.ops.wm.save_as_mainfile("INVOKE_AREA")
247
248 class WM_OT_save_reload(Operator):
249     """Save and Reload the current blend file"""
250     bl_idname = "wm.save_reload"
251     bl_label = "Save & Reload"
252
253     def execute(self, context):
254
255         path = bpy.data.filepath
256         save_reload(self, context, path)
257         return {'FINISHED'}
258
259 def button_save_reload(self, context):
260
261     preferences = context.user_preferences.addons[__name__].preferences
262
263     if preferences.use_file_save_reload:
264         self.layout.separator()
265         self.layout.operator(
266             WM_OT_save_reload.bl_idname,
267             text="Save & Reload",
268             icon='FILE_REFRESH')
269 # // FEATURE: Save & Reload
270
271 # FEATURE: Current Frame
272 def button_frame_current(self, context):
273
274     preferences = context.user_preferences.addons[__name__].preferences
275     scene = context.scene
276
277     if preferences.use_frame_current:
278         self.layout.separator()
279         self.layout.prop(
280             scene, "frame_current",
281             text="Set Current Frame")
282 # // FEATURE: Current Frame
283
284 # FEATURE: Timeline Time + Frames Left
285 def label_timeline_extra_info(self, context):
286
287     preferences = context.user_preferences.addons[__name__].preferences
288     layout = self.layout
289     scene = context.scene
290
291     if preferences.use_timeline_extra_info:
292         row = layout.row(align=True)
293
294         # Check for preview range
295         frame_start = scene.frame_preview_start if scene.use_preview_range else scene.frame_start
296         frame_end = scene.frame_preview_end if scene.use_preview_range else scene.frame_end
297         
298         row.label(text="%s / %s" % (bpy.utils.smpte_from_frame(scene.frame_current - frame_start),
299                         bpy.utils.smpte_from_frame(frame_end - frame_start)))
300
301         if (scene.frame_current > frame_end):
302             row.label(text="%s Frames Ahead" % ((frame_end - scene.frame_current) * -1))
303         elif (scene.frame_current == frame_start):
304             row.label(text="Start Frame (%s left)" % (frame_end - scene.frame_current))
305         elif (scene.frame_current == frame_end):
306             row.label(text="%s End Frame" % scene.frame_current)
307         else:
308             row.label(text="%s Frames Left" % (frame_end - scene.frame_current))
309
310 # // FEATURE: Timeline Time + Frames Left
311
312 # FEATURE: Directory Current Blend
313 class FILE_OT_directory_current_blend(Operator):
314     """Go to the directory of the currently open blend file"""
315     bl_idname = "file.directory_current_blend"
316     bl_label = "Current Blend's Folder"
317
318     def execute(self, context):
319         bpy.ops.file.select_bookmark(dir='//')
320         return {'FINISHED'}
321
322 def button_directory_current_blend(self, context):
323
324     if bpy.data.filepath:
325         self.layout.operator(
326             FILE_OT_directory_current_blend.bl_idname,
327             text="Current Blend's Folder",
328             icon='APPEND_BLEND')
329 # // FEATURE: Directory Current Blend
330
331 # FEATURE: Libraries panel on file browser
332 class FILE_PT_libraries(Panel):
333     bl_space_type = 'FILE_BROWSER'
334     bl_region_type = 'CHANNELS'
335     bl_label = "Libraries"
336
337     def draw(self, context):
338         layout = self.layout
339
340         libs = bpy.data.libraries
341         libslist = []
342
343         # Build the list of folders from libraries
344         import os
345
346         for lib in libs:
347             directory_name = os.path.dirname(lib.filepath)
348             libslist.append(directory_name)
349
350         # Remove duplicates and sort by name
351         libslist = set(libslist)
352         libslist = sorted(libslist)
353
354         # Draw the box with libs
355         
356         row = layout.row()
357         box = row.box()
358        
359         if libslist:
360             for filepath in libslist:
361                 if filepath != '//':
362                     row = box.row()
363                     row.alignment = 'LEFT'
364                     props = row.operator(
365                         FILE_OT_directory_go_to.bl_idname,
366                         text=filepath, icon="BOOKMARKS",
367                         emboss=False)
368                     props.filepath = filepath
369         else:
370             box.label(text='No libraries loaded')
371
372 class FILE_OT_directory_go_to(Operator):
373     """Go to this library's directory"""
374     bl_idname = "file.directory_go_to"
375     bl_label = "Go To"
376     
377     filepath = bpy.props.StringProperty(subtype="FILE_PATH")
378
379     def execute(self, context):
380
381         bpy.ops.file.select_bookmark(dir=self.filepath)
382         return {'FINISHED'}
383     
384 # FEATURE: Node Templates
385 class NODE_OT_AddTemplateVignette(Operator):
386     bl_idname = "node.template_add_vignette"
387     bl_label = "Add Vignette"
388     bl_description = "Add a vignette effect"
389     bl_options = {'REGISTER', 'UNDO'}
390
391     @classmethod
392     def poll(cls, context):
393         space = context.space_data
394         return space.type == 'NODE_EDITOR' \
395                 and space.node_tree is not None \
396                 and space.tree_type == 'CompositorNodeTree'
397
398     # used as reference the setup scene script from master nazgul
399     def _setupNodes(self, context):
400         scene = context.scene
401         space = context.space_data
402         tree = scene.node_tree
403
404         bpy.ops.node.select_all(action='DESELECT')
405
406         ellipse = tree.nodes.new(type='CompositorNodeEllipseMask')
407         ellipse.width = 0.8
408         ellipse.height = 0.4
409         blur = tree.nodes.new(type='CompositorNodeBlur')
410         blur.use_relative = True
411         blur.factor_x = 30
412         blur.factor_y = 50
413         ramp = tree.nodes.new(type='CompositorNodeValToRGB')
414         ramp.color_ramp.interpolation = 'B_SPLINE'
415         ramp.color_ramp.elements[1].color = (0.6, 0.6, 0.6, 1)
416
417         overlay = tree.nodes.new(type='CompositorNodeMixRGB')
418         overlay.blend_type = 'OVERLAY'
419         overlay.inputs[0].default_value = 0.8
420         overlay.inputs[1].default_value = (0.5, 0.5, 0.5, 1)
421
422         tree.links.new(ellipse.outputs["Mask"],blur.inputs["Image"])
423         tree.links.new(blur.outputs["Image"],ramp.inputs[0])
424         tree.links.new(ramp.outputs["Image"],overlay.inputs[2])
425
426         if tree.nodes.active:
427             blur.location = tree.nodes.active.location
428             blur.location += Vector((330.0, -250.0))
429         else:
430             blur.location += Vector((space.cursor_location[0], space.cursor_location[1]))
431
432         ellipse.location = blur.location
433         ellipse.location += Vector((-300.0, 0))
434
435         ramp.location = blur.location
436         ramp.location += Vector((175.0, 0))
437
438         overlay.location = ramp.location
439         overlay.location += Vector((240.0, 275.0))
440
441         for node in {ellipse, blur, ramp, overlay}:
442             node.select = True
443             node.show_preview = False
444
445         bpy.ops.node.join()
446
447         frame = ellipse.parent
448         frame.label = 'Vignette'
449         frame.use_custom_color = True
450         frame.color = (0.783538, 0.0241576, 0.0802198)
451         
452         overlay.parent = None
453         overlay.label = 'Vignette Overlay'
454
455     def execute(self, context):
456         self._setupNodes(context)
457
458         return {'FINISHED'}
459
460 # Node Templates Menu
461 class NODE_MT_amaranth_templates(Menu):
462     bl_idname = 'NODE_MT_amaranth_templates'
463     bl_space_type = 'NODE_EDITOR'
464     bl_label = "Templates"
465     bl_description = "List of Amaranth Templates"
466
467     def draw(self, context):
468         layout = self.layout
469         layout.operator(
470             NODE_OT_AddTemplateVignette.bl_idname,
471             text="Vignette",
472             icon='COLOR')
473
474 def node_templates_pulldown(self, context):
475
476     if context.space_data.tree_type == 'CompositorNodeTree':
477         layout = self.layout
478         row = layout.row(align=True)
479         row.scale_x = 1.3
480         row.menu("NODE_MT_amaranth_templates",
481             icon="RADIO")
482 # // FEATURE: Node Templates
483
484 def node_stats(self,context):
485     if context.scene.node_tree:
486         tree_type = context.space_data.tree_type
487         nodes = context.scene.node_tree.nodes
488         nodes_total = len(nodes.keys())
489         nodes_selected = 0
490         for n in nodes:
491             if n.select:
492                 nodes_selected = nodes_selected + 1
493
494         if tree_type == 'CompositorNodeTree':
495             layout = self.layout
496             row = layout.row(align=True)
497             row.label(text="Nodes: %s/%s" % (nodes_selected, str(nodes_total)))
498
499 # FEATURE: Simplify Compo Nodes
500 class NODE_PT_simplify(Panel):
501     '''Simplify Compositor Panel'''
502     bl_space_type = 'NODE_EDITOR'
503     bl_region_type = 'UI'
504     bl_label = 'Simplify'
505     bl_options = {'DEFAULT_CLOSED'}
506
507     @classmethod
508     def poll(cls, context):
509         space = context.space_data
510         return space.type == 'NODE_EDITOR' \
511                 and space.node_tree is not None \
512                 and space.tree_type == 'CompositorNodeTree'
513
514     def draw(self, context):
515         layout = self.layout
516         node_tree = context.scene.node_tree
517
518         if node_tree is not None:
519             layout.prop(node_tree, 'types')
520             layout.operator(NODE_OT_toggle_mute.bl_idname,
521                 text="Turn On" if node_tree.toggle_mute else "Turn Off",
522                 icon='RESTRICT_VIEW_OFF' if node_tree.toggle_mute else 'RESTRICT_VIEW_ON')
523         
524             if node_tree.types == 'VECBLUR':
525                 layout.label(text="This will also toggle the Vector pass {}".format(
526                                     "on" if node_tree.toggle_mute else "off"), icon="INFO")
527
528 class NODE_OT_toggle_mute(Operator):
529     """"""
530     bl_idname = "node.toggle_mute"
531     bl_label = "Toggle Mute"
532
533     def execute(self, context):
534         scene = context.scene
535         node_tree = scene.node_tree
536         node_type = node_tree.types
537         rlayers = scene.render
538         
539         if not 'amaranth_pass_vector' in scene.keys():
540             scene['amaranth_pass_vector'] = []
541         
542         #can't extend() the list, so make a dummy one
543         pass_vector = scene['amaranth_pass_vector']
544
545         if not pass_vector:
546             pass_vector = []
547
548         if node_tree.toggle_mute:
549             for node in node_tree.nodes:
550                 if node_type == 'ALL':
551                     node.mute = node.status
552                 if node.type == node_type:
553                     node.mute = node.status
554                 if node_type == 'VECBLUR':
555                     for layer in rlayers.layers:
556                         if layer.name in pass_vector:
557                             layer.use_pass_vector = True
558                             pass_vector.remove(layer.name)
559
560                 node_tree.toggle_mute = False
561
562         else:
563             for node in node_tree.nodes:
564                 if node_type == 'ALL':
565                     node.mute = True
566                 if node.type == node_type:
567                     node.status = node.mute
568                     node.mute = True
569                 if node_type == 'VECBLUR':
570                     for layer in rlayers.layers:
571                         if layer.use_pass_vector:
572                             pass_vector.append(layer.name)
573                             layer.use_pass_vector = False
574                             pass
575
576                 node_tree.toggle_mute = True
577
578         # Write back to the custom prop
579         pass_vector = sorted(set(pass_vector))
580         scene['amaranth_pass_vector'] = pass_vector
581
582         return {'FINISHED'}
583         
584
585 # FEATURE: OB/MA ID panel in Node Editor
586 class NODE_PT_indices(Panel):
587     '''Object / Material Indices Panel'''
588     bl_space_type = 'NODE_EDITOR'
589     bl_region_type = 'UI'
590     bl_label = 'Object / Material Indices'
591     bl_options = {'DEFAULT_CLOSED'}
592
593     @classmethod
594     def poll(cls, context):
595         node = context.active_node
596         return node and node.type == 'ID_MASK'
597
598     def draw(self, context):
599         layout = self.layout
600
601         objects = bpy.data.objects
602         materials = bpy.data.materials
603         node = context.active_node
604
605         show_ob_id = False
606         show_ma_id = False
607         matching_ids = False
608
609         if context.active_object:
610             ob_act = context.active_object
611         else:
612             ob_act = False
613
614         for ob in objects:
615             if ob and ob.pass_index > 0:
616                 show_ob_id = True
617         for ma in materials:
618             if ma and ma.pass_index > 0:
619                 show_ma_id = True
620         row = layout.row(align=True)  
621         row.prop(node, 'index', text="Mask Index")
622         row.prop(node, 'use_matching_indices', text="Only Matching IDs")
623         
624         layout.separator()
625
626         if not show_ob_id and not show_ma_id:
627             layout.label(text="No objects or materials indices so far.", icon="INFO")
628
629         if show_ob_id:
630             split = layout.split()
631             col = split.column()
632             col.label(text="Object Name")
633             split.label(text="ID Number")
634             row = layout.row()
635             for ob in objects:
636                 icon = "OUTLINER_DATA_" + ob.type
637                 if ob.library:
638                     icon = "LIBRARY_DATA_DIRECT"
639                 elif ob.is_library_indirect:
640                     icon = "LIBRARY_DATA_INDIRECT"
641
642                 if ob and node.use_matching_indices \
643                       and ob.pass_index == node.index \
644                       and ob.pass_index != 0:
645                     matching_ids = True
646                     row.label(
647                       text="[{}]".format(ob.name)
648                           if ob_act and ob.name == ob_act.name else ob.name,
649                       icon=icon)
650                     row.label(text="%s" % ob.pass_index)
651                     row = layout.row()
652
653                 elif ob and not node.use_matching_indices \
654                         and ob.pass_index > 0:
655
656                     matching_ids = True
657                     row.label(
658                       text="[{}]".format(ob.name)
659                           if ob_act and ob.name == ob_act.name else ob.name,
660                       icon=icon)
661                     row.label(text="%s" % ob.pass_index)
662                     row = layout.row()
663
664             if node.use_matching_indices and not matching_ids:
665                 row.label(text="No objects with ID %s" % node.index, icon="INFO")
666
667             layout.separator()
668
669         if show_ma_id:
670             split = layout.split()
671             col = split.column()
672             col.label(text="Material Name")
673             split.label(text="ID Number")
674             row = layout.row()
675
676             for ma in materials:
677                 icon = "BLANK1"
678                 if ma.use_nodes:
679                     icon = "NODETREE"
680                 elif ma.library:
681                     icon = "LIBRARY_DATA_DIRECT"
682                     if ma.is_library_indirect:
683                         icon = "LIBRARY_DATA_INDIRECT"
684
685                 if ma and node.use_matching_indices \
686                       and ma.pass_index == node.index \
687                       and ma.pass_index != 0:
688                     matching_ids = True
689                     row.label(text="%s" % ma.name, icon=icon)
690                     row.label(text="%s" % ma.pass_index)
691                     row = layout.row()
692
693                 elif ma and not node.use_matching_indices \
694                         and ma.pass_index > 0:
695
696                     matching_ids = True
697                     row.label(text="%s" % ma.name, icon=icon)
698                     row.label(text="%s" % ma.pass_index)
699                     row = layout.row()
700
701             if node.use_matching_indices and not matching_ids:
702                 row.label(text="No materials with ID %s" % node.index, icon="INFO")
703
704
705 # // FEATURE: OB/MA ID panel in Node Editor
706
707 # FEATURE: Unsimplify on render
708 @persistent
709 def unsimplify_render_pre(scene):
710     render = scene.render
711     scene.simplify_status = render.use_simplify
712
713     if scene.use_unsimplify_render:
714         render.use_simplify = False
715
716 @persistent
717 def unsimplify_render_post(scene):
718     render = scene.render
719     render.use_simplify = scene.simplify_status
720
721 def unsimplify_ui(self,context):
722     scene = bpy.context.scene
723     self.layout.prop(scene, 'use_unsimplify_render')
724 # //FEATURE: Unsimplify on render
725
726 # FEATURE: Extra Info Stats
727 def stats_scene(self, context):
728
729     preferences = context.user_preferences.addons[__name__].preferences
730
731     if preferences.use_scene_stats:
732         scenes_count = str(len(bpy.data.scenes))
733         cameras_count = str(len(bpy.data.cameras))
734         cameras_selected = 0
735         meshlights = 0
736         meshlights_visible = 0
737     
738         for ob in context.scene.objects:
739             if ob.material_slots:
740                 for ma in ob.material_slots:
741                     if ma.material:
742                         if ma.material.node_tree:
743                             for no in ma.material.node_tree.nodes:
744                                 if no.type == 'EMISSION':
745                                     for ou in no.outputs:
746                                         if ou.links:
747                                             meshlights = meshlights + 1
748                                             if ob in context.visible_objects:
749                                                 meshlights_visible = meshlights_visible + 1
750                                             break
751             if ob in context.selected_objects:
752                 if ob.type == 'CAMERA':
753                     cameras_selected = cameras_selected + 1
754     
755         meshlights_string = '| Meshlights:{}/{}'.format(meshlights_visible, meshlights)
756     
757         row = self.layout.row(align=True)
758         row.label(text="Scenes:{} | Cameras:{}/{} {}".format(
759                    scenes_count, cameras_selected, cameras_count,
760                    meshlights_string if context.scene.render.engine == 'CYCLES' else ''))
761
762 # //FEATURE: Extra Info Stats
763
764 # FEATURE: Camera Bounds as Render Border
765 class VIEW3D_OT_render_border_camera(Operator):
766     """Set camera bounds as render border"""
767     bl_idname = "view3d.render_border_camera"
768     bl_label = "Camera as Render Border"
769
770     @classmethod
771     def poll(cls, context):
772         return context.space_data.region_3d.view_perspective == 'CAMERA'
773
774     def execute(self, context):
775         render = context.scene.render
776         render.use_border = True
777         render.border_min_x = 0
778         render.border_min_y = 0
779         render.border_max_x = 1
780         render.border_max_y = 1
781
782         return {'FINISHED'}
783
784 def button_render_border_camera(self, context):
785
786     view3d = context.space_data.region_3d
787     
788     if view3d.view_perspective == 'CAMERA':
789         layout = self.layout
790         layout.separator()
791         layout.operator(VIEW3D_OT_render_border_camera.bl_idname,
792                         text="Camera as Render Border", icon="FULLSCREEN_ENTER")
793
794 # //FEATURE: Camera Bounds as Render Border
795
796 # FEATURE: Passepartout options on W menu
797 def button_camera_passepartout(self, context):
798
799     view3d = context.space_data.region_3d
800     cam = context.scene.camera.data
801     
802     if view3d.view_perspective == 'CAMERA':
803         layout = self.layout
804         if cam.show_passepartout:
805             layout.prop(cam, "passepartout_alpha", text="Passepartout")
806         else:
807             layout.prop(cam, "show_passepartout")
808
809 # FEATURE: Show Only Render with Alt+Shift+Z
810 class VIEW3D_OT_show_only_render(Operator):
811     bl_idname = "view3d.show_only_render"
812     bl_label = "Show Only Render"
813
814     def execute(self, context):
815         space = bpy.context.space_data
816         
817         if space.show_only_render:
818             space.show_only_render = False
819         else:
820             space.show_only_render = True
821         return {'FINISHED'}
822
823
824 # FEATURE: Display Active Image Node on Image Editor
825 # Made by Sergey Sharybin, tweaks from Bassam Kurdali
826 image_nodes = {"CompositorNodeImage",
827                "ShaderNodeTexImage",
828                "ShaderNodeTexEnvironment"}
829
830 class NODE_OT_show_active_node_image(Operator):
831     """Show active image node image in the image editor"""
832     bl_idname = "node.show_active_node_image"
833     bl_label = "Show Active Node Node"
834     bl_options = {'UNDO'}
835
836     def execute(self, context):
837         preferences = context.user_preferences.addons[__name__].preferences
838         if preferences.use_image_node_display:
839             if context.active_node:
840                 active_node = context.active_node
841                 if active_node.bl_idname in image_nodes and active_node.image:
842                     for area in context.screen.areas:
843                         if area.type == "IMAGE_EDITOR":
844                             for space in area.spaces:
845                                 if space.type == "IMAGE_EDITOR":
846                                     space.image = active_node.image
847                             break
848     
849         return {'FINISHED'}
850 # // FEATURE: Display Active Image Node on Image Editor
851
852 # FEATURE: Select Meshlights
853 class OBJECT_OT_select_meshlights(Operator):
854     """Select light emitting meshes"""
855     bl_idname = "object.select_meshlights"
856     bl_label = "Select Meshlights"
857     bl_options = {'UNDO'}
858
859     @classmethod
860     def poll(cls, context):
861         return context.scene.render.engine == 'CYCLES'
862
863     def execute(self, context):
864         # Deselect everything first
865         bpy.ops.object.select_all(action='DESELECT')
866
867         for ob in context.scene.objects:
868             if ob.material_slots:
869                 for ma in ob.material_slots:
870                     if ma.material:
871                         if ma.material.node_tree:
872                             for no in ma.material.node_tree.nodes:
873                                 if no.type == 'EMISSION':
874                                     ob.select = True
875                                     context.scene.objects.active = ob
876
877         if not context.selected_objects and not context.scene.objects.active:
878             self.report({'INFO'}, "No meshlights to select")
879
880         return {'FINISHED'}
881
882 def button_select_meshlights(self, context):
883     
884     if context.scene.render.engine == 'CYCLES':
885         self.layout.operator('object.select_meshlights', icon="LAMP_SUN")
886 # // FEATURE: Select Meshlights
887
888 # FEATURE: Mesh Symmetry Tools by Sergey Sharybin
889 class MESH_OT_find_asymmetric(Operator):
890     """
891     Find asymmetric vertices
892     """
893
894     bl_idname = "mesh.find_asymmetric"
895     bl_label = "Find Asymmetric"
896     bl_options = {'UNDO', 'REGISTER'}
897
898     @classmethod
899     def poll(cls, context):
900         object = context.object
901         if object:
902             return object.mode == 'EDIT' and object.type == 'MESH'
903         return False
904
905     def execute(self, context):
906         threshold = 1e-6
907
908         object = context.object
909         bm = bmesh.from_edit_mesh(object.data)
910
911         # Deselect all the vertices
912         for v in bm.verts:
913             v.select = False
914
915         for v1 in bm.verts:
916             if abs(v1.co[0]) < threshold:
917                 continue
918
919             mirror_found = False
920             for v2 in bm.verts:
921                 if v1 == v2:
922                     continue
923                 if v1.co[0] * v2.co[0] > 0.0:
924                     continue
925
926                 mirror_coord = Vector(v2.co)
927                 mirror_coord[0] *= -1
928                 if (mirror_coord - v1.co).length_squared < threshold:
929                     mirror_found = True
930                     break
931             if not mirror_found:
932                 v1.select = True
933
934         bm.select_flush_mode()
935
936         bmesh.update_edit_mesh(object.data)
937
938         return {'FINISHED'}
939
940 class MESH_OT_make_symmetric(Operator):
941     """
942     Make symmetric
943     """
944
945     bl_idname = "mesh.make_symmetric"
946     bl_label = "Make Symmetric"
947     bl_options = {'UNDO', 'REGISTER'}
948
949     @classmethod
950     def poll(cls, context):
951         object = context.object
952         if object:
953             return object.mode == 'EDIT' and object.type == 'MESH'
954         return False
955
956     def execute(self, context):
957         threshold = 1e-6
958
959         object = context.object
960         bm = bmesh.from_edit_mesh(object.data)
961
962         for v1 in bm.verts:
963             if v1.co[0] < threshold:
964                 continue
965             if not v1.select:
966                 continue
967
968             closest_vert = None
969             closest_distance = -1
970             for v2 in bm.verts:
971                 if v1 == v2:
972                     continue
973                 if v2.co[0] > threshold:
974                     continue
975                 if not v2.select:
976                     continue
977
978                 mirror_coord = Vector(v2.co)
979                 mirror_coord[0] *= -1
980                 distance = (mirror_coord - v1.co).length_squared
981                 if closest_vert is None or distance < closest_distance:
982                     closest_distance = distance
983                     closest_vert = v2
984
985             if closest_vert:
986                 closest_vert.select = False
987                 closest_vert.co = Vector(v1.co)
988                 closest_vert.co[0] *= -1
989             v1.select = False
990
991         for v1 in bm.verts:
992             if v1.select:
993                 closest_vert = None
994                 closest_distance = -1
995                 for v2 in bm.verts:
996                     if v1 != v2:
997                         mirror_coord = Vector(v2.co)
998                         mirror_coord[0] *= -1
999                         distance = (mirror_coord - v1.co).length_squared
1000                         if closest_vert is None or distance < closest_distance:
1001                             closest_distance = distance
1002                             closest_vert = v2
1003                 if closest_vert:
1004                     v1.select = False
1005                     v1.co = Vector(closest_vert.co)
1006                     v1.co[0] *= -1
1007
1008         bm.select_flush_mode()
1009         bmesh.update_edit_mesh(object.data)
1010
1011         return {'FINISHED'}
1012 # // FEATURE: Mesh Symmetry Tools by Sergey Sharybin
1013
1014 # FEATURE: Cycles Render Samples per Scene
1015 def render_cycles_scene_samples(self, context):
1016
1017     layout = self.layout
1018
1019     scenes = bpy.data.scenes
1020     scene = context.scene
1021     cscene = scene.cycles
1022
1023     if (len(bpy.data.scenes) > 1):
1024         layout.separator()
1025
1026         layout.label(text="Samples Per Scene:")
1027
1028         if cscene.progressive == 'PATH':
1029             for s in bpy.data.scenes:
1030                 if s != scene:
1031                     if s.render.engine == 'CYCLES':
1032                         cscene = s.cycles
1033         
1034                         split = layout.split()
1035                         col = split.column()
1036                         sub = col.column(align=True)
1037         
1038                         sub.label(text="%s" % s.name)
1039         
1040                         col = split.column()
1041                         sub = col.column(align=True)
1042                         sub.prop(cscene, "samples", text="Samples")
1043                     else:
1044                         layout.label(text="Scene: '%s' is not using Cycles" % s.name)
1045         else:
1046             for s in bpy.data.scenes:
1047                 if s != scene:
1048                     if s.render.engine == 'CYCLES':
1049                         cscene = s.cycles
1050         
1051                         split = layout.split()
1052                         col = split.column()
1053                         sub = col.column(align=True)
1054         
1055                         sub.label(text="%s" % s.name)
1056         
1057                         col = split.column()
1058                         sub = col.column(align=True)
1059                         sub.prop(cscene, "aa_samples", text="AA Samples")
1060                     else:
1061                         layout.label(text="Scene: '%s' is not using Cycles" % s.name)
1062 # // FEATURE: Cycles Render Samples per Scene
1063
1064 # FEATURE: Motion Paths Extras
1065 class POSE_OT_paths_clear_all(Operator):
1066     """Clear motion paths from all bones"""
1067     bl_idname = "pose.paths_clear_all"
1068     bl_label = "Clear All Motion Paths"
1069     bl_options = {'UNDO'}
1070
1071     @classmethod
1072     def poll(cls, context):
1073         return context.mode == 'POSE'
1074
1075     def execute(self, context):
1076         #silly but works
1077         for b in context.object.data.bones:
1078             b.select = True
1079             bpy.ops.pose.paths_clear()
1080             b.select = False
1081         return {'FINISHED'}
1082
1083 class POSE_OT_paths_frame_match(Operator):
1084     """Match Start/End frame of scene to motion path range"""
1085     bl_idname = "pose.paths_frame_match"
1086     bl_label = "Match Frame Range"
1087     bl_options = {'UNDO'}
1088
1089     def execute(self, context):
1090         avs = context.object.pose.animation_visualization
1091         scene = context.scene
1092
1093         if avs.motion_path.type == 'RANGE':
1094             if scene.use_preview_range:
1095                 avs.motion_path.frame_start = scene.frame_preview_start
1096                 avs.motion_path.frame_end = scene.frame_preview_end
1097             else:
1098                 avs.motion_path.frame_start = scene.frame_start
1099                 avs.motion_path.frame_end = scene.frame_end
1100
1101         else:
1102             if scene.use_preview_range:
1103                 avs.motion_path.frame_before = scene.frame_preview_start
1104                 avs.motion_path.frame_after = scene.frame_preview_end
1105             else:
1106                 avs.motion_path.frame_before = scene.frame_start
1107                 avs.motion_path.frame_after = scene.frame_end
1108
1109         return {'FINISHED'}
1110
1111 def pose_motion_paths_ui(self, context):
1112
1113     layout = self.layout
1114     scene = context.scene
1115     avs = context.object.pose.animation_visualization
1116     if context.active_pose_bone:
1117         mpath = context.active_pose_bone.motion_path
1118     layout.separator()    
1119     layout.label(text="Motion Paths Extras:")
1120
1121     split = layout.split()
1122
1123     col = split.column(align=True)
1124
1125     if context.selected_pose_bones:
1126         if mpath:
1127             sub = col.row(align=True)
1128             sub.operator("pose.paths_update", text="Update Path", icon='BONE_DATA')
1129             sub.operator("pose.paths_clear", text="", icon='X')
1130         else:
1131             col.operator("pose.paths_calculate", text="Calculate Path", icon='BONE_DATA')
1132     else:
1133         col.label(text="Select Bones First", icon="ERROR")
1134
1135     col = split.column(align=True)
1136     col.operator(POSE_OT_paths_frame_match.bl_idname,
1137         text="{}".format( "Set Preview Frame Range"
1138                 if scene.use_preview_range else "Set Frame Range"),
1139         icon="{}".format("PREVIEW_RANGE"
1140                 if scene.use_preview_range else "TIME"))
1141
1142     col = layout.column()
1143     row = col.row(align=True)
1144
1145     if avs.motion_path.type == 'RANGE':
1146         row.prop(avs.motion_path, "frame_start", text="Start")
1147         row.prop(avs.motion_path, "frame_end", text="End")
1148     else:
1149         row.prop(avs.motion_path, "frame_before", text="Before")
1150         row.prop(avs.motion_path, "frame_after", text="After")
1151
1152     layout.separator()
1153     layout.operator(POSE_OT_paths_clear_all.bl_idname, icon="X")
1154 # // FEATURE: Motion Paths Extras
1155
1156 # FEATURE: Final Render Resolution Display
1157 def render_final_resolution_ui(self, context):
1158
1159     rd = context.scene.render
1160     layout = self.layout
1161
1162     final_res_x = (rd.resolution_x * rd.resolution_percentage) / 100
1163     final_res_y = (rd.resolution_y * rd.resolution_percentage) / 100
1164
1165     if rd.use_border:
1166        final_res_x_border = round((final_res_x * (rd.border_max_x - rd.border_min_x)))
1167        final_res_y_border = round((final_res_y * (rd.border_max_y - rd.border_min_y)))
1168        layout.label(text="Final Resolution: {} x {} [Border: {} x {}]".format(
1169              str(final_res_x)[:-2], str(final_res_y)[:-2],
1170              str(final_res_x_border), str(final_res_y_border)))
1171     else:
1172         layout.label(text="Final Resolution: {} x {}".format(
1173              str(final_res_x)[:-2], str(final_res_y)[:-2]))
1174 # // FEATURE: Final Render Resolution Display
1175
1176 # FEATURE: Shader Nodes Extra Info
1177 def node_shader_extra(self, context):
1178
1179     if context.space_data.tree_type == 'ShaderNodeTree':
1180         ob = context.active_object
1181         snode = context.space_data
1182         layout = self.layout
1183
1184         if ob and snode.shader_type != 'WORLD':
1185             if ob.type == 'LAMP':
1186                 layout.label(text="%s" % ob.name,
1187                              icon="LAMP_%s" % ob.data.type)        
1188             else:
1189                 layout.label(text="%s" % ob.name,
1190                              icon="OUTLINER_DATA_%s" % ob.type)
1191              
1192
1193 # // FEATURE: Shader Nodes Extra Info
1194
1195 # FEATURE: Scene Debug
1196 class SCENE_OT_cycles_shader_list_nodes(Operator):
1197     """List Cycles materials containing a specific shader"""
1198     bl_idname = "scene.cycles_list_nodes"
1199     bl_label = "List Materials"
1200
1201     @classmethod
1202     def poll(cls, context):
1203         return context.scene.render.engine == 'CYCLES'
1204
1205     def execute(self, context):
1206         node_type = context.scene.amaranth_cycles_node_types
1207         count_ma = 0
1208         roughness = False
1209         materials = []
1210         global materials
1211
1212         print("\n=== Cycles Shader Type: %s === \n" % node_type)
1213
1214         for ma in bpy.data.materials:
1215             if ma.node_tree:
1216                 nodes = ma.node_tree.nodes
1217                 for no in nodes:
1218                     if no.type == node_type:
1219                         for ou in no.outputs:
1220                             if ou.links:
1221                                 if no.type in ['BSDF_GLOSSY','BSDF_DIFFUSE','BSDF_GLASS']:
1222                                     roughness = 'R: %.4f' % no.inputs['Roughness'].default_value
1223                                 else:
1224                                     roughness = False
1225                             else:
1226                                 print('Note: \nOutput from "%s" node' % node_type,
1227                                       'in material "%s"' % ma.name,
1228                                       'not connected',
1229                                       '\n')
1230
1231                             materials = list(set(materials))
1232
1233                             count_ma = len(materials)
1234
1235                             if ma.name not in materials:
1236                                 materials.append('%s%s [%s] %s%s' % (
1237                                     '[L] ' if ma.library else '',
1238                                     ma.name, ma.users,
1239                                     '[F]' if ma.use_fake_user else '',
1240                                     ' - [%s]' % roughness if roughness else ''))
1241
1242         if count_ma == 0:
1243             print("* No materials with nodes type %s found" % node_type)
1244
1245         else:
1246             print("* A total of %s materials using %s was found \n" % (
1247                     count_ma + 1, node_type))
1248
1249             count = 0
1250
1251             for mat in materials:
1252                 print('%02d. %s' % (count + 1, materials[count]))
1253                 count = count + 1
1254             print("\n")
1255
1256         materials = list(set(materials))
1257
1258         return {'FINISHED'}
1259
1260 class SCENE_OT_cycles_shader_list_nodes_clear(Operator):
1261     """Clear the list below"""
1262     bl_idname = "scene.cycles_list_nodes_clear"
1263     bl_label = "Clear Materials List"
1264     
1265     def execute(self, context):
1266         materials[:] = []
1267         return {'FINISHED'}
1268
1269 class SCENE_OT_amaranth_debug_lamp_select(Operator):
1270     '''Select Lamp'''
1271     bl_idname = "scene.amaranth_debug_lamp_select"
1272     bl_label = "Select Lamp"
1273     lamp = bpy.props.StringProperty()
1274  
1275     def execute(self, context):
1276         if self.lamp:
1277             lamp = bpy.data.objects[self.lamp]
1278
1279             bpy.ops.object.select_all(action='DESELECT')
1280             lamp.select = True
1281             context.scene.objects.active = lamp
1282
1283         return{'FINISHED'}    
1284
1285
1286 class SCENE_PT_scene_debug(Panel):
1287     '''Scene Debug'''
1288     bl_label = 'Scene Debug'
1289     bl_space_type = "PROPERTIES"
1290     bl_region_type = "WINDOW"
1291     bl_context = "scene"
1292
1293     def draw(self, context):
1294         layout = self.layout
1295         scene = context.scene
1296         objects =  bpy.data.objects
1297         ob_act = context.active_object
1298         images = bpy.data.images
1299         lamps = bpy.data.lamps
1300         images_missing = []
1301         list_lamps = scene.amaranth_debug_scene_list_lamps
1302         list_missing_images = scene.amaranth_debug_scene_list_missing_images
1303         engine = scene.render.engine
1304
1305         if engine == 'CYCLES':
1306
1307             layout.label(text="List Cycles Materials:")
1308
1309             split = layout.split(percentage=0.5)
1310             split.prop(scene, 'amaranth_cycles_node_types')
1311
1312             row = split.row(align=True)
1313             row.operator(SCENE_OT_cycles_shader_list_nodes.bl_idname,
1314                             icon="SORTALPHA")
1315             row.operator(SCENE_OT_cycles_shader_list_nodes_clear.bl_idname,
1316                             icon="X", text="")
1317
1318             try:
1319                 materials
1320             except NameError:
1321                 layout.label(text="No materials to show", icon="INFO")
1322             else:
1323                 count = 0
1324                 layout.label(text="%s %s found" % (len(materials),
1325                     'material' if len(materials) < 2 else 'materials'), icon="INFO")
1326                 for mat in materials:
1327                     count = count + 1
1328                     layout.label(text='%s' % (materials[count-1]), icon="MATERIAL")
1329
1330         # List Lamps
1331         box = layout.box()
1332         row = box.row(align=True)
1333         split = row.split()
1334         col = split.column()
1335         
1336         if lamps:
1337             row = col.row(align=True)
1338             row.alignment = 'LEFT'
1339             row.prop(scene, 'amaranth_debug_scene_list_lamps',
1340                         icon="%s" % 'TRIA_DOWN' if list_lamps else 'TRIA_RIGHT',
1341                         emboss=False)
1342
1343             if objects and list_lamps:
1344                 row = box.row(align=True)
1345                 split = row.split(percentage=0.42)
1346                 col = split.column()
1347                 col.label(text="Name")
1348
1349                 split = split.split(percentage=0.1)
1350                 col = split.column()
1351                 col.label(text="", icon="BLANK1")
1352                 if engine in ['CYCLES', 'BLENDER_RENDER']:
1353                     if engine == 'BLENDER_RENDER':
1354                         split = split.split(percentage=0.7)
1355                     else:
1356                         split = split.split(percentage=0.35)
1357                     col = split.column()
1358                     col.label(text="Samples")
1359
1360                 if engine == 'CYCLES':
1361                     split = split.split(percentage=0.35)
1362                     col = split.column()
1363                     col.label(text="Size")
1364
1365                 split = split.split(percentage=0.8)
1366                 col = split.column()
1367                 col.label(text="Visibility")
1368
1369                 for ob in objects:
1370                     if ob and ob.type == 'LAMP':
1371                         lamp = ob.data
1372                         clamp = ob.data.cycles
1373
1374                         row = box.row(align=True)
1375                         split = row.split(percentage=0.5)
1376                         col = split.column()
1377                         row = col.row()
1378                         row.alignment = 'LEFT'
1379                         row.active = ob.name in context.scene.objects
1380                         row.operator("scene.amaranth_debug_lamp_select",
1381                                     text='%s %s%s' % (
1382                                         " [L] " if ob.library else "",
1383                                         ob.name,
1384                                         "" if ob.name in context.scene.objects else " [Not in Scene]"),
1385                                     icon="LAMP_%s" % ob.data.type,
1386                                     emboss=False).lamp = ob.name
1387
1388                         if engine == 'CYCLES':
1389                             split = split.split(percentage=0.35)
1390                             col = split.column()
1391                             if scene.cycles.progressive == 'BRANCHED_PATH':
1392                                 col.prop(clamp, "samples", text="")
1393                             if scene.cycles.progressive == 'PATH':
1394                                col.label(text="N/A")
1395                            
1396                         if engine == 'BLENDER_RENDER':
1397                             split = split.split(percentage=0.7)
1398                             col = split.column()
1399                             if lamp.type == 'HEMI':
1400                                 col.label(text="Not Available")
1401                             elif lamp.type == 'AREA' and lamp.shadow_method == 'RAY_SHADOW':
1402                                 row = col.row(align=True)
1403                                 row.prop(lamp, "shadow_ray_samples_x", text="X")
1404                                 if lamp.shape == 'RECTANGLE':
1405                                     row.prop(lamp, "shadow_ray_samples_y", text="Y")
1406                             elif lamp.shadow_method == 'RAY_SHADOW':
1407                                 col.prop(lamp, "shadow_ray_samples", text="Ray Samples")
1408                             elif lamp.shadow_method == 'BUFFER_SHADOW':
1409                                 col.prop(lamp, "shadow_buffer_samples", text="Buffer Samples")
1410                             else:
1411                                 col.label(text="No Shadow")
1412
1413                         if engine == 'CYCLES':
1414                             split = split.split(percentage=0.4)
1415                             col = split.column()    
1416                             if lamp.type in ['POINT','SUN', 'SPOT']:
1417                                 col.label(text="%.2f" % lamp.shadow_soft_size)
1418                             elif lamp.type == 'HEMI':
1419                                 col.label(text="N/A")
1420                             else:
1421                                 col.label(text="%.2f" % lamp.size)
1422
1423                         split = split.split(percentage=0.8)
1424                         col = split.column()
1425                         row = col.row(align=True)
1426                         row.prop(ob, "hide", text="", emboss=False)
1427                         row.prop(ob, "hide_render", text="", emboss=False)
1428
1429                         split = split.split(percentage=0.3)
1430                         col = split.column()
1431                         col.label(text="", icon="%s" % "TRIA_LEFT" if ob == ob_act else "BLANK1")
1432
1433         else:
1434             row = col.row(align=True)
1435             row.alignment = 'LEFT'
1436             row.label(text="Lamps List", icon="RIGHTARROW_THIN")
1437
1438             split = split.split()
1439             col = split.column()
1440
1441             col.label(text="No Lamps", icon="LAMP_DATA")
1442
1443         # List Missing Images
1444         if images:
1445             import os.path
1446
1447             for im in images:
1448                 if im.type not in ['UV_TEST', 'RENDER_RESULT', 'COMPOSITING']: 
1449                     if not os.path.exists(bpy.path.abspath(im.filepath, library=im.library)):
1450                         images_missing.append(["%s%s [%s]%s" % (
1451                             '[L] ' if im.library else '',
1452                             im.name, im.users,
1453                             ' [F]' if im.use_fake_user else ''),
1454                             im.filepath if im.filepath else 'No Filepath'])
1455
1456             box = layout.box()
1457             row = box.row(align=True)
1458             split = row.split()
1459             col = split.column()
1460
1461             if images_missing:
1462                 row = col.row(align=True)
1463                 row.alignment = 'LEFT'
1464                 row.prop(scene, 'amaranth_debug_scene_list_missing_images',
1465                             icon="%s" % 'TRIA_DOWN' if list_missing_images else 'TRIA_RIGHT',
1466                             emboss=False)
1467
1468                 split = split.split()
1469                 col = split.column()
1470
1471                 col.label(text="%s missing %s" % (
1472                              str(len(images_missing)),
1473                              'image' if len(images_missing) == 1 else 'images'),
1474                              icon="ERROR")
1475
1476                 if list_missing_images:
1477                     col = box.column(align=True)
1478                     for mis in images_missing:
1479                         col.label(text=mis[0],
1480                          icon="IMAGE_DATA")
1481                         col.label(text=mis[1], icon="BLANK1")
1482                         col.separator()
1483             else:
1484                 row = col.row(align=True)
1485                 row.alignment = 'LEFT'
1486                 row.label(text="Great! No missing images", icon="RIGHTARROW_THIN")
1487
1488                 split = split.split()
1489                 col = split.column()
1490
1491                 col.label(text="%s %s loading correctly" % (
1492                              str(len(images)),
1493                              'image' if len(images) == 1 else 'images'),
1494                              icon="IMAGE_DATA")
1495
1496 # // FEATURE: Scene Debug
1497
1498 # FEATURE: Color Management Presets
1499 class SCENE_MT_color_management_presets(Menu):
1500     """List of Color Management presets"""
1501     bl_label = "Color Management Presets"
1502     preset_subdir = "color"
1503     preset_operator = "script.execute_preset"
1504     draw = Menu.draw_preset
1505
1506
1507 class AddPresetColorManagement(AddPresetBase, Operator):
1508     """Add or remove a Color Management preset"""
1509     bl_idname = "scene.color_management_preset_add"
1510     bl_label = "Add Color Management Preset"
1511     preset_menu = "SCENE_MT_color_management_presets"
1512
1513     preset_defines = [
1514         "scene = bpy.context.scene"
1515     ]
1516
1517     preset_values = [
1518         "scene.view_settings.view_transform",
1519         "scene.display_settings.display_device",
1520         "scene.view_settings.exposure",
1521         "scene.view_settings.gamma",
1522         "scene.view_settings.look",
1523         "scene.view_settings.use_curve_mapping",
1524         "scene.sequencer_colorspace_settings.name",
1525     ]
1526
1527     preset_subdir = "color"
1528
1529 def ui_color_management_presets(self, context):
1530     
1531     layout = self.layout
1532
1533     row = layout.row(align=True)
1534     row.menu("SCENE_MT_color_management_presets", text=bpy.types.SCENE_MT_color_management_presets.bl_label)
1535     row.operator("scene.color_management_preset_add", text="", icon="ZOOMIN")
1536     row.operator("scene.color_management_preset_add", text="", icon="ZOOMOUT").remove_active = True
1537     layout.separator()
1538 # // FEATURE: Color Management Presets
1539
1540
1541 classes = (SCENE_MT_color_management_presets,
1542            AddPresetColorManagement,
1543            SCENE_PT_scene_debug,
1544            SCENE_OT_refresh,
1545            SCENE_OT_cycles_shader_list_nodes,
1546            SCENE_OT_cycles_shader_list_nodes_clear,
1547            SCENE_OT_amaranth_debug_lamp_select,
1548            WM_OT_save_reload,
1549            MESH_OT_find_asymmetric,
1550            MESH_OT_make_symmetric,
1551            NODE_OT_AddTemplateVignette,
1552            NODE_MT_amaranth_templates,
1553            FILE_OT_directory_current_blend,
1554            FILE_OT_directory_go_to,
1555            NODE_PT_indices,
1556            NODE_PT_simplify,
1557            NODE_OT_toggle_mute,
1558            NODE_OT_show_active_node_image,
1559            VIEW3D_OT_render_border_camera,
1560            VIEW3D_OT_show_only_render,
1561            OBJECT_OT_select_meshlights,
1562            POSE_OT_paths_clear_all,
1563            POSE_OT_paths_frame_match,
1564            FILE_PT_libraries)
1565
1566 addon_keymaps = []
1567
1568 kmi_defs = (
1569     ('wm.call_menu', 'W', False, False, False, (('name', NODE_MT_amaranth_templates.bl_idname),)),
1570 )
1571
1572 def register():
1573
1574     bpy.utils.register_class(AmaranthToolsetPreferences)
1575
1576     # UI: Register the panel
1577     init_properties()
1578     for c in classes:
1579         bpy.utils.register_class(c)
1580
1581     bpy.types.VIEW3D_MT_object_specials.append(button_refresh)
1582     bpy.types.VIEW3D_MT_object_specials.append(button_render_border_camera)
1583     bpy.types.VIEW3D_MT_object_specials.append(button_camera_passepartout)
1584
1585     bpy.types.INFO_MT_file.append(button_save_reload)
1586     bpy.types.INFO_HT_header.append(stats_scene)
1587
1588     bpy.types.VIEW3D_MT_object_specials.append(button_frame_current) # Current Frame
1589     bpy.types.VIEW3D_MT_pose_specials.append(button_frame_current)
1590     bpy.types.VIEW3D_MT_select_object.append(button_select_meshlights)
1591
1592     bpy.types.TIME_HT_header.append(label_timeline_extra_info) # Timeline Extra Info
1593
1594     bpy.types.NODE_HT_header.append(node_templates_pulldown)
1595     bpy.types.NODE_HT_header.append(node_stats)
1596     bpy.types.NODE_HT_header.append(node_shader_extra)
1597
1598     bpy.types.CyclesRender_PT_sampling.append(render_cycles_scene_samples)
1599
1600     bpy.types.FILEBROWSER_HT_header.append(button_directory_current_blend)
1601
1602     bpy.types.SCENE_PT_simplify.append(unsimplify_ui)
1603     bpy.types.CyclesScene_PT_simplify.append(unsimplify_ui)
1604
1605     bpy.types.DATA_PT_display.append(pose_motion_paths_ui)
1606
1607     bpy.types.RENDER_PT_dimensions.append(render_final_resolution_ui)
1608
1609     bpy.types.SCENE_PT_color_management.prepend(ui_color_management_presets)
1610
1611     bpy.app.handlers.render_pre.append(unsimplify_render_pre)
1612     bpy.app.handlers.render_post.append(unsimplify_render_post)
1613
1614     wm = bpy.context.window_manager
1615     kc = wm.keyconfigs.addon
1616     if kc:
1617         km = kc.keymaps.new(name='Window')
1618         kmi = km.keymap_items.new('scene.refresh', 'F5', 'PRESS', shift=False, ctrl=False)
1619         kmi = km.keymap_items.new('wm.save_reload', 'W', 'PRESS', shift=True, ctrl=True)
1620
1621         km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
1622         kmi = km.keymap_items.new('view3d.show_only_render', 'Z', 'PRESS', shift=True, alt=True)
1623
1624         km = kc.keymaps.new(name='Graph Editor', space_type='GRAPH_EDITOR')
1625         kmi = km.keymap_items.new('wm.context_set_enum', 'TAB', 'PRESS', ctrl=True)
1626         kmi.properties.data_path = 'area.type'
1627         kmi.properties.value = 'DOPESHEET_EDITOR'
1628
1629         km = kc.keymaps.new(name='Dopesheet', space_type='DOPESHEET_EDITOR')
1630         kmi = km.keymap_items.new('wm.context_set_enum', 'TAB', 'PRESS', ctrl=True)
1631         kmi.properties.data_path = 'area.type'
1632         kmi.properties.value = 'GRAPH_EDITOR'
1633
1634         km = kc.keymaps.new(name='Dopesheet', space_type='DOPESHEET_EDITOR')
1635         kmi = km.keymap_items.new('wm.context_toggle_enum', 'TAB', 'PRESS', shift=True)
1636         kmi.properties.data_path = 'space_data.mode'
1637         kmi.properties.value_1 = 'ACTION'
1638         kmi.properties.value_2 = 'DOPESHEET'
1639
1640         km = kc.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
1641         km.keymap_items.new("node.show_active_node_image", 'ACTIONMOUSE', 'RELEASE')
1642         km.keymap_items.new("node.show_active_node_image", 'SELECTMOUSE', 'RELEASE')
1643
1644         addon_keymaps.append((km, kmi))
1645
1646         # copypasted from the awesome node efficiency tools, future hotkeys proof!
1647         km = kc.keymaps.new(name='Node Editor', space_type="NODE_EDITOR")
1648         for (identifier, key, CTRL, SHIFT, ALT, props) in kmi_defs:
1649             kmi = km.keymap_items.new(identifier, key, 'PRESS', ctrl=CTRL, shift=SHIFT, alt=ALT)
1650             if props:
1651                 for prop, value in props:
1652                     setattr(kmi.properties, prop, value)
1653             addon_keymaps.append((km, kmi))
1654
1655 def unregister():
1656
1657     bpy.utils.unregister_class(AmaranthToolsetPreferences)
1658
1659     for c in classes:
1660         bpy.utils.unregister_class(c)
1661
1662     bpy.types.VIEW3D_MT_object_specials.remove(button_refresh)
1663     bpy.types.VIEW3D_MT_object_specials.remove(button_render_border_camera)
1664     bpy.types.VIEW3D_MT_object_specials.remove(button_camera_passepartout)
1665
1666     bpy.types.INFO_MT_file.remove(button_save_reload)
1667     bpy.types.INFO_HT_header.remove(stats_scene)
1668
1669     bpy.types.VIEW3D_MT_object_specials.remove(button_frame_current)
1670     bpy.types.VIEW3D_MT_pose_specials.remove(button_frame_current)
1671     bpy.types.VIEW3D_MT_select_object.remove(button_select_meshlights)
1672
1673     bpy.types.TIME_HT_header.remove(label_timeline_extra_info)
1674
1675     bpy.types.NODE_HT_header.remove(node_templates_pulldown)
1676     bpy.types.NODE_HT_header.remove(node_stats)
1677     bpy.types.NODE_HT_header.remove(node_shader_extra)
1678
1679     bpy.types.CyclesRender_PT_sampling.remove(render_cycles_scene_samples)
1680
1681     bpy.types.FILEBROWSER_HT_header.remove(button_directory_current_blend)
1682
1683     bpy.types.SCENE_PT_simplify.remove(unsimplify_ui)
1684     bpy.types.CyclesScene_PT_simplify.remove(unsimplify_ui)
1685
1686     bpy.types.DATA_PT_display.remove(pose_motion_paths_ui)
1687
1688     bpy.types.RENDER_PT_dimensions.remove(render_final_resolution_ui)
1689
1690     bpy.types.SCENE_PT_color_management.remove(ui_color_management_presets)
1691
1692     bpy.app.handlers.render_pre.remove(unsimplify_render_pre)
1693     bpy.app.handlers.render_post.remove(unsimplify_render_post)
1694
1695     for km, kmi in addon_keymaps:
1696         km.keymap_items.remove(kmi)
1697     addon_keymaps.clear()
1698     
1699     clear_properties()
1700
1701 if __name__ == "__main__":
1702     register()