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