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