Newer
Older
Pablo Vazquez
committed
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Amaranth Toolset",
"author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin",
"blender": (2, 70),
"location": "Everywhere!",
Pablo Vazquez
committed
"description": "A collection of tools and settings to improve productivity",
"warning": "",
"wiki_url": "http://pablovazquez.org/amaranth",
"tracker_url": "",
"category": "Scene"}
import bpy
from bpy.types import Operator, AddonPreferences, Panel, Menu
from bpy.props import (BoolProperty, EnumProperty,
FloatProperty, IntProperty,
StringProperty)
Pablo Vazquez
committed
from mathutils import Vector
from bpy.app.handlers import persistent
from bl_operators.presets import AddPresetBase
Pablo Vazquez
committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# Preferences
class AmaranthToolsetPreferences(AddonPreferences):
bl_idname = __name__
use_frame_current = BoolProperty(
name="Current Frame Slider",
description="Set the current frame from the Specials menu in the 3D View",
default=True,
)
use_file_save_reload = BoolProperty(
name="Save & Reload File",
description="File menu > Save & Reload, or Ctrl + Shift + W",
default=True,
)
use_scene_refresh = BoolProperty(
name="Refresh Scene",
description="Specials Menu [W], or hit F5",
default=True,
)
use_timeline_extra_info = BoolProperty(
name="Timeline Extra Info",
description="Timeline Header",
default=True,
)
use_image_node_display = BoolProperty(
name="Active Image Node in Editor",
description="Display active node image in image editor",
default=True,
)
use_scene_stats = BoolProperty(
name="Extra Scene Statistics",
description="Display extra scene statistics in Info editor's header",
default=True,
)
def draw(self, context):
layout = self.layout
layout.label(
text="Here you can enable or disable specific tools, "
"in case they interfere with others or are just plain annoying")
split = layout.split(percentage=0.25)
col = split.column()
sub = col.column(align=True)
sub.label(text="3D View", icon="VIEW3D")
sub.prop(self, "use_frame_current")
sub.prop(self, "use_scene_refresh")
sub.separator()
sub.label(text="General", icon="SCENE_DATA")
sub.prop(self, "use_file_save_reload")
sub.prop(self, "use_timeline_extra_info")
sub.prop(self, "use_scene_stats")
sub.separator()
CoDEmanX
committed
sub.label(text="Nodes Editor", icon="NODETREE")
Pablo Vazquez
committed
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
sub.prop(self, "use_image_node_display")
col = split.column()
sub = col.column(align=True)
sub.label(text="")
sub.label(
text="Set the current frame from the Specials menu in the 3D View [W]")
sub.label(
text="Refresh the current Scene. Hotkey: F5 or in Specials menu [W]")
sub.separator()
sub.label(text="") # General
sub.label(
text="Quickly save and reload the current file (no warning!). "
"File menu or Ctrl+Shift+W")
sub.label(
text="SMPTE Timecode and frames left/ahead on Timeline's header")
sub.label(
text="Display extra statistics for Scenes, Cameras, and Meshlights (Cycles)")
sub.separator()
sub.label(text="") # Nodes
sub.label(
text="When selecting an Image node, display it on the Image editor "
"(if any)")
# Properties
def init_properties():
scene = bpy.types.Scene
node = bpy.types.Node
nodes_compo = bpy.types.CompositorNodeTree
scene.use_unsimplify_render = BoolProperty(
Pablo Vazquez
committed
default=False,
name="Unsimplify Render",
description="Disable Simplify during render")
scene.simplify_status = BoolProperty(default=False)
Pablo Vazquez
committed
node.use_matching_indices = BoolProperty(
Pablo Vazquez
committed
default=True,
description="If disabled, display all available indices")
Pablo Vazquez
committed
("ALL", "All Types", "", 0),
("BLUR", "Blur", "", 1),
("BOKEHBLUR", "Bokeh Blur", "", 2),
("VECBLUR", "Vector Blur", "", 3),
("DEFOCUS", "Defocus", "", 4),
("R_LAYERS", "Render Layer", "", 5)
]
nodes_compo.types = EnumProperty(
items=nodes_compo_types, name = "Types")
Pablo Vazquez
committed
nodes_compo.toggle_mute = BoolProperty(default=False)
node.status = BoolProperty(default=False)
# Scene Debug
# Cycles Node Types
cycles_shader_node_types = [
("BSDF_DIFFUSE", "Diffuse BSDF", "", 0),
("BSDF_GLOSSY", "Glossy BSDF", "", 1),
("BSDF_TRANSPARENT", "Transparent BSDF", "", 2),
("BSDF_REFRACTION", "Refraction BSDF", "", 3),
("BSDF_GLASS", "Glass BSDF", "", 4),
("BSDF_TRANSLUCENT", "Translucent BSDF", "", 5),
("BSDF_ANISOTROPIC", "Anisotropic BSDF", "", 6),
("BSDF_VELVET", "Velvet BSDF", "", 7),
("BSDF_TOON", "Toon BSDF", "", 8),
("SUBSURFACE_SCATTERING", "Subsurface Scattering", "", 9),
("EMISSION", "Emission", "", 10),
("BSDF_HAIR", "Hair BSDF", "", 11),
("BACKGROUND", "Background", "", 12),
("AMBIENT_OCCLUSION", "Ambient Occlusion", "", 13),
("HOLDOUT", "Holdout", "", 14),
("VOLUME_ABSORPTION", "Volume Absorption", "", 15),
("VOLUME_SCATTER", "Volume Scatter", "", 16)
items=cycles_shader_node_types, name = "Shader")
scene.amaranth_debug_scene_list_lamps = BoolProperty(
default=False,
name="Lamps List",
description="Display a list of all the lamps")
scene.amaranth_debug_scene_list_missing_images = BoolProperty(
default=False,
name="List Missing Images",
description="Display a list of all the missing images")
scene.amaranth_cycles_list_sampling = BoolProperty(
default=False,
name="Samples Per:")
bpy.types.ShaderNodeNormal.normal_vector = prop_normal_vector
bpy.types.CompositorNodeNormal.normal_vector = prop_normal_vector
bpy.types.CyclesRenderSettings.use_samples_final = BoolProperty(
name="Use Final Render Samples",
description="Use current shader samples as final render samples",
default=False,)
Pablo Vazquez
committed
def clear_properties():
props = (
"use_unsimplify_render",
"simplify_status",
"use_matching_indices",
"use_simplify_nodes_vector",
"status",
"types",
"toggle_mute",
"amaranth_cycles_node_types",
"amaranth_debug_scene_list_lamps",
"amaranth_debug_scene_list_missing_images",
"amarath_cycles_list_sampling",
"normal_vector",
"use_samples_final"
Pablo Vazquez
committed
)
Pablo Vazquez
committed
wm = bpy.context.window_manager
for p in props:
if p in wm:
del wm[p]
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# Some settings are bound to be saved on a startup py file
def amaranth_text_startup(context):
amth_text_name = "AmaranthStartup.py"
amth_text_exists = False
global amth_text
try:
for tx in bpy.data.texts:
if tx.name == amth_text_name:
amth_text_exists = True
amth_text = bpy.data.texts[amth_text_name]
break
else:
amth_text_exists = False
bpy.ops.text.new()
amth_text = bpy.data.texts[-1]
amth_text.name = amth_text_name
amth_text.write("# Amaranth Startup Script\nimport bpy\n\n")
amth_text.use_module = True
break
return amth_text_exists
except AttributeError:
return None
Pablo Vazquez
committed
# FEATURE: Refresh Scene!
Pablo Vazquez
committed
"""Refresh the current scene"""
bl_idname = "scene.refresh"
bl_label = "Refresh!"
Pablo Vazquez
committed
def execute(self, context):
preferences = context.user_preferences.addons[__name__].preferences
scene = context.scene
Pablo Vazquez
committed
# Changing the frame is usually the best way to go
scene.frame_current = scene.frame_current
self.report({"INFO"}, "Scene Refreshed!")
Pablo Vazquez
committed
return {'FINISHED'}
def button_refresh(self, context):
preferences = context.user_preferences.addons[__name__].preferences
if preferences.use_scene_refresh:
self.layout.separator()
self.layout.operator(
Pablo Vazquez
committed
text="Refresh!",
icon='FILE_REFRESH')
# // FEATURE: Refresh Scene!
# FEATURE: Save & Reload
def save_reload(self, context, path):
if path:
bpy.ops.wm.save_mainfile()
self.report({'INFO'}, "Saved & Reloaded")
bpy.ops.wm.open_mainfile("EXEC_DEFAULT", filepath=path)
else:
bpy.ops.wm.save_as_mainfile("INVOKE_AREA")
Pablo Vazquez
committed
"""Save and Reload the current blend file"""
bl_idname = "wm.save_reload"
bl_label = "Save & Reload"
def execute(self, context):
path = bpy.data.filepath
save_reload(self, context, path)
return {'FINISHED'}
def button_save_reload(self, context):
preferences = context.user_preferences.addons[__name__].preferences
if preferences.use_file_save_reload:
self.layout.separator()
self.layout.operator(
Pablo Vazquez
committed
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
text="Save & Reload",
icon='FILE_REFRESH')
# // FEATURE: Save & Reload
# FEATURE: Current Frame
def button_frame_current(self, context):
preferences = context.user_preferences.addons[__name__].preferences
scene = context.scene
if preferences.use_frame_current:
self.layout.separator()
self.layout.prop(
scene, "frame_current",
text="Set Current Frame")
# // FEATURE: Current Frame
# FEATURE: Timeline Time + Frames Left
def label_timeline_extra_info(self, context):
preferences = context.user_preferences.addons[__name__].preferences
layout = self.layout
scene = context.scene
if preferences.use_timeline_extra_info:
row = layout.row(align=True)
# Check for preview range
frame_start = scene.frame_preview_start if scene.use_preview_range else scene.frame_start
frame_end = scene.frame_preview_end if scene.use_preview_range else scene.frame_end
Pablo Vazquez
committed
row.label(text="%s / %s" % (bpy.utils.smpte_from_frame(scene.frame_current - frame_start),
bpy.utils.smpte_from_frame(frame_end - frame_start)))
if (scene.frame_current > frame_end):
row.label(text="%s Frames Ahead" % ((frame_end - scene.frame_current) * -1))
elif (scene.frame_current == frame_start):
row.label(text="Start Frame (%s left)" % (frame_end - scene.frame_current))
Pablo Vazquez
committed
elif (scene.frame_current == frame_end):
row.label(text="%s End Frame" % scene.frame_current)
else:
row.label(text="%s Frames Left" % (frame_end - scene.frame_current))
# // FEATURE: Timeline Time + Frames Left
# FEATURE: Directory Current Blend
class AMTH_FILE_OT_directory_current_blend(Operator):
Pablo Vazquez
committed
"""Go to the directory of the currently open blend file"""
bl_idname = "file.directory_current_blend"
bl_label = "Current Blend's Folder"
def execute(self, context):
bpy.ops.file.select_bookmark(dir='//')
return {'FINISHED'}
def button_directory_current_blend(self, context):
if bpy.data.filepath:
self.layout.operator(
Pablo Vazquez
committed
text="Current Blend's Folder",
icon='APPEND_BLEND')
# // FEATURE: Directory Current Blend
# FEATURE: Libraries panel on file browser
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'CHANNELS'
bl_label = "Libraries"
def draw(self, context):
layout = self.layout
libs = bpy.data.libraries
libslist = []
# Build the list of folders from libraries
import os
for lib in libs:
directory_name = os.path.dirname(lib.filepath)
libslist.append(directory_name)
# Remove duplicates and sort by name
libslist = set(libslist)
libslist = sorted(libslist)
# Draw the box with libs
row = layout.row()
box = row.box()
for filepath in libslist:
if filepath != '//':
row.alignment = 'LEFT'
props = row.operator(
text=filepath, icon="BOOKMARKS",
emboss=False)
props.filepath = filepath
else:
box.label(text='No libraries loaded')
"""Go to this library's directory"""
bl_idname = "file.directory_go_to"
bl_label = "Go To"
filepath = bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.ops.file.select_bookmark(dir=self.filepath)
return {'FINISHED'}
Pablo Vazquez
committed
# FEATURE: Node Templates
class AMTH_NODE_OT_AddTemplateVignette(Operator):
Pablo Vazquez
committed
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
bl_idname = "node.template_add_vignette"
bl_label = "Add Vignette"
bl_description = "Add a vignette effect"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'NODE_EDITOR' \
and space.node_tree is not None \
and space.tree_type == 'CompositorNodeTree'
# used as reference the setup scene script from master nazgul
def _setupNodes(self, context):
scene = context.scene
space = context.space_data
tree = scene.node_tree
bpy.ops.node.select_all(action='DESELECT')
ellipse = tree.nodes.new(type='CompositorNodeEllipseMask')
ellipse.width = 0.8
ellipse.height = 0.4
blur = tree.nodes.new(type='CompositorNodeBlur')
blur.use_relative = True
blur.factor_x = 30
blur.factor_y = 50
ramp = tree.nodes.new(type='CompositorNodeValToRGB')
ramp.color_ramp.interpolation = 'B_SPLINE'
ramp.color_ramp.elements[1].color = (0.6, 0.6, 0.6, 1)
overlay = tree.nodes.new(type='CompositorNodeMixRGB')
overlay.blend_type = 'OVERLAY'
overlay.inputs[0].default_value = 0.8
overlay.inputs[1].default_value = (0.5, 0.5, 0.5, 1)
tree.links.new(ellipse.outputs["Mask"],blur.inputs["Image"])
tree.links.new(blur.outputs["Image"],ramp.inputs[0])
tree.links.new(ramp.outputs["Image"],overlay.inputs[2])
if tree.nodes.active:
blur.location = tree.nodes.active.location
blur.location += Vector((330.0, -250.0))
else:
blur.location += Vector((space.cursor_location[0], space.cursor_location[1]))
ellipse.location = blur.location
ellipse.location += Vector((-300.0, 0))
ramp.location = blur.location
ramp.location += Vector((175.0, 0))
overlay.location = ramp.location
overlay.location += Vector((240.0, 275.0))
for node in {ellipse, blur, ramp, overlay}:
node.select = True
node.show_preview = False
bpy.ops.node.join()
frame = ellipse.parent
frame.label = 'Vignette'
frame.use_custom_color = True
frame.color = (0.783538, 0.0241576, 0.0802198)
Pablo Vazquez
committed
overlay.parent = None
overlay.label = 'Vignette Overlay'
def execute(self, context):
self._setupNodes(context)
return {'FINISHED'}
# Node Templates Menu
class AMTH_NODE_MT_amaranth_templates(Menu):
bl_idname = 'AMTH_NODE_MT_amaranth_templates'
Pablo Vazquez
committed
bl_space_type = 'NODE_EDITOR'
bl_label = "Templates"
bl_description = "List of Amaranth Templates"
def draw(self, context):
layout = self.layout
layout.operator(
Pablo Vazquez
committed
text="Vignette",
icon='COLOR')
def node_templates_pulldown(self, context):
if context.space_data.tree_type == 'CompositorNodeTree':
layout = self.layout
row = layout.row(align=True)
row.scale_x = 1.3
Pablo Vazquez
committed
# // FEATURE: Node Templates
def node_stats(self,context):
if context.scene.node_tree:
tree_type = context.space_data.tree_type
nodes = context.scene.node_tree.nodes
nodes_total = len(nodes.keys())
nodes_selected = 0
for n in nodes:
if n.select:
nodes_selected = nodes_selected + 1
if tree_type == 'CompositorNodeTree':
layout = self.layout
row = layout.row(align=True)
row.label(text="Nodes: %s/%s" % (nodes_selected, str(nodes_total)))
# FEATURE: Simplify Compo Nodes
Pablo Vazquez
committed
'''Simplify Compositor Panel'''
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_label = 'Simplify'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'NODE_EDITOR' \
and space.node_tree is not None \
and space.tree_type == 'CompositorNodeTree'
Pablo Vazquez
committed
def draw(self, context):
layout = self.layout
node_tree = context.scene.node_tree
if node_tree is not None:
layout.prop(node_tree, 'types')
layout.operator(AMTH_NODE_OT_toggle_mute.bl_idname,
Pablo Vazquez
committed
text="Turn On" if node_tree.toggle_mute else "Turn Off",
icon='RESTRICT_VIEW_OFF' if node_tree.toggle_mute else 'RESTRICT_VIEW_ON')
Pablo Vazquez
committed
if node_tree.types == 'VECBLUR':
layout.label(text="This will also toggle the Vector pass {}".format(
"on" if node_tree.toggle_mute else "off"), icon="INFO")
Pablo Vazquez
committed
""""""
bl_idname = "node.toggle_mute"
bl_label = "Toggle Mute"
def execute(self, context):
scene = context.scene
node_tree = scene.node_tree
node_type = node_tree.types
rlayers = scene.render
Pablo Vazquez
committed
if not 'amaranth_pass_vector' in scene.keys():
scene['amaranth_pass_vector'] = []
Pablo Vazquez
committed
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
#can't extend() the list, so make a dummy one
pass_vector = scene['amaranth_pass_vector']
if not pass_vector:
pass_vector = []
if node_tree.toggle_mute:
for node in node_tree.nodes:
if node_type == 'ALL':
node.mute = node.status
if node.type == node_type:
node.mute = node.status
if node_type == 'VECBLUR':
for layer in rlayers.layers:
if layer.name in pass_vector:
layer.use_pass_vector = True
pass_vector.remove(layer.name)
node_tree.toggle_mute = False
else:
for node in node_tree.nodes:
if node_type == 'ALL':
node.mute = True
if node.type == node_type:
node.status = node.mute
node.mute = True
if node_type == 'VECBLUR':
for layer in rlayers.layers:
if layer.use_pass_vector:
pass_vector.append(layer.name)
layer.use_pass_vector = False
pass
node_tree.toggle_mute = True
# Write back to the custom prop
pass_vector = sorted(set(pass_vector))
scene['amaranth_pass_vector'] = pass_vector
return {'FINISHED'}
Pablo Vazquez
committed
# FEATURE: OB/MA ID panel in Node Editor
Pablo Vazquez
committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
'''Object / Material Indices Panel'''
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_label = 'Object / Material Indices'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
node = context.active_node
return node and node.type == 'ID_MASK'
def draw(self, context):
layout = self.layout
objects = bpy.data.objects
materials = bpy.data.materials
node = context.active_node
show_ob_id = False
show_ma_id = False
matching_ids = False
if context.active_object:
ob_act = context.active_object
else:
ob_act = False
for ob in objects:
if ob and ob.pass_index > 0:
show_ob_id = True
for ma in materials:
if ma and ma.pass_index > 0:
show_ma_id = True
Pablo Vazquez
committed
row.prop(node, 'index', text="Mask Index")
row.prop(node, 'use_matching_indices', text="Only Matching IDs")
Pablo Vazquez
committed
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
layout.separator()
if not show_ob_id and not show_ma_id:
layout.label(text="No objects or materials indices so far.", icon="INFO")
if show_ob_id:
split = layout.split()
col = split.column()
col.label(text="Object Name")
split.label(text="ID Number")
row = layout.row()
for ob in objects:
icon = "OUTLINER_DATA_" + ob.type
if ob.library:
icon = "LIBRARY_DATA_DIRECT"
elif ob.is_library_indirect:
icon = "LIBRARY_DATA_INDIRECT"
if ob and node.use_matching_indices \
and ob.pass_index == node.index \
and ob.pass_index != 0:
matching_ids = True
row.label(
text="[{}]".format(ob.name)
if ob_act and ob.name == ob_act.name else ob.name,
icon=icon)
row.label(text="%s" % ob.pass_index)
row = layout.row()
elif ob and not node.use_matching_indices \
and ob.pass_index > 0:
matching_ids = True
row.label(
text="[{}]".format(ob.name)
if ob_act and ob.name == ob_act.name else ob.name,
icon=icon)
row.label(text="%s" % ob.pass_index)
row = layout.row()
if node.use_matching_indices and not matching_ids:
row.label(text="No objects with ID %s" % node.index, icon="INFO")
layout.separator()
if show_ma_id:
split = layout.split()
col = split.column()
col.label(text="Material Name")
split.label(text="ID Number")
row = layout.row()
for ma in materials:
icon = "BLANK1"
if ma.use_nodes:
icon = "NODETREE"
elif ma.library:
icon = "LIBRARY_DATA_DIRECT"
if ma.is_library_indirect:
icon = "LIBRARY_DATA_INDIRECT"
if ma and node.use_matching_indices \
and ma.pass_index == node.index \
and ma.pass_index != 0:
matching_ids = True
row.label(text="%s" % ma.name, icon=icon)
row.label(text="%s" % ma.pass_index)
row = layout.row()
elif ma and not node.use_matching_indices \
and ma.pass_index > 0:
matching_ids = True
row.label(text="%s" % ma.name, icon=icon)
row.label(text="%s" % ma.pass_index)
row = layout.row()
if node.use_matching_indices and not matching_ids:
row.label(text="No materials with ID %s" % node.index, icon="INFO")
# // FEATURE: OB/MA ID panel in Node Editor
# FEATURE: Unsimplify on render
@persistent
def unsimplify_render_pre(scene):
render = scene.render
scene.simplify_status = render.use_simplify
if scene.use_unsimplify_render:
render.use_simplify = False
@persistent
def unsimplify_render_post(scene):
render = scene.render
render.use_simplify = scene.simplify_status
def unsimplify_ui(self,context):
scene = bpy.context.scene
self.layout.prop(scene, 'use_unsimplify_render')
# //FEATURE: Unsimplify on render
# FEATURE: Extra Info Stats
def stats_scene(self, context):
preferences = context.user_preferences.addons[__name__].preferences
if preferences.use_scene_stats:
scenes_count = str(len(bpy.data.scenes))
cameras_count = str(len(bpy.data.cameras))
cameras_selected = 0
meshlights = 0
meshlights_visible = 0
Pablo Vazquez
committed
for ob in context.scene.objects:
if ob.material_slots:
for ma in ob.material_slots:
if ma.material:
if ma.material.node_tree:
for no in ma.material.node_tree.nodes:
if no.type == 'EMISSION':
Pablo Vazquez
committed
if ob in context.selected_objects:
if ob.type == 'CAMERA':
Pablo Vazquez
committed
meshlights_string = '| Meshlights:{}/{}'.format(meshlights_visible, meshlights)
Pablo Vazquez
committed
row = self.layout.row(align=True)
row.label(text="Scenes:{} | Cameras:{}/{} {}".format(
scenes_count, cameras_selected, cameras_count,
meshlights_string if context.scene.render.engine == 'CYCLES' else ''))
# //FEATURE: Extra Info Stats
# FEATURE: Camera Bounds as Render Border
class AMTH_VIEW3D_OT_render_border_camera(Operator):
Pablo Vazquez
committed
"""Set camera bounds as render border"""
bl_idname = "view3d.render_border_camera"
bl_label = "Camera as Render Border"
@classmethod
def poll(cls, context):
return context.space_data.region_3d.view_perspective == 'CAMERA'
def execute(self, context):
render = context.scene.render
render.use_border = True
render.border_min_x = 0
render.border_min_y = 0
render.border_max_x = 1
render.border_max_y = 1
return {'FINISHED'}
def button_render_border_camera(self, context):
view3d = context.space_data.region_3d
Pablo Vazquez
committed
if view3d.view_perspective == 'CAMERA':
layout = self.layout
layout.separator()
layout.operator(AMTH_VIEW3D_OT_render_border_camera.bl_idname,
Pablo Vazquez
committed
text="Camera as Render Border", icon="FULLSCREEN_ENTER")
# //FEATURE: Camera Bounds as Render Border
# FEATURE: Passepartout options on W menu
def button_camera_passepartout(self, context):
view3d = context.space_data.region_3d
cam = context.scene.camera.data
Pablo Vazquez
committed
if view3d.view_perspective == 'CAMERA':
layout = self.layout
if cam.show_passepartout:
layout.prop(cam, "passepartout_alpha", text="Passepartout")
else:
layout.prop(cam, "show_passepartout")
# FEATURE: Show Only Render with Alt+Shift+Z
Pablo Vazquez
committed
bl_idname = "view3d.show_only_render"
bl_label = "Show Only Render"
def execute(self, context):
space = bpy.context.space_data
Pablo Vazquez
committed
if space.show_only_render:
space.show_only_render = False
else:
space.show_only_render = True
return {'FINISHED'}
# FEATURE: Display Active Image Node on Image Editor
# Made by Sergey Sharybin, tweaks from Bassam Kurdali
image_nodes = {"CompositorNodeImage",
"ShaderNodeTexImage",
"ShaderNodeTexEnvironment"}
class AMTH_NODE_OT_show_active_node_image(Operator):
Pablo Vazquez
committed
"""Show active image node image in the image editor"""
bl_idname = "node.show_active_node_image"
bl_label = "Show Active Node Node"
bl_options = {'UNDO'}
def execute(self, context):
preferences = context.user_preferences.addons[__name__].preferences
if preferences.use_image_node_display:
if context.active_node:
active_node = context.active_node
if active_node.bl_idname in image_nodes and active_node.image:
for area in context.screen.areas:
if area.type == "IMAGE_EDITOR":
for space in area.spaces:
if space.type == "IMAGE_EDITOR":
space.image = active_node.image
break
Pablo Vazquez
committed
return {'FINISHED'}
# // FEATURE: Display Active Image Node on Image Editor
# FEATURE: Select Meshlights
class AMTH_OBJECT_OT_select_meshlights(Operator):
Pablo Vazquez
committed
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
"""Select light emitting meshes"""
bl_idname = "object.select_meshlights"
bl_label = "Select Meshlights"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return context.scene.render.engine == 'CYCLES'
def execute(self, context):
# Deselect everything first
bpy.ops.object.select_all(action='DESELECT')
for ob in context.scene.objects:
if ob.material_slots:
for ma in ob.material_slots:
if ma.material:
if ma.material.node_tree:
for no in ma.material.node_tree.nodes:
if no.type == 'EMISSION':
ob.select = True
context.scene.objects.active = ob
if not context.selected_objects and not context.scene.objects.active:
self.report({'INFO'}, "No meshlights to select")
return {'FINISHED'}
def button_select_meshlights(self, context):
Pablo Vazquez
committed
if context.scene.render.engine == 'CYCLES':
self.layout.operator('object.select_meshlights', icon="LAMP_SUN")
# // FEATURE: Select Meshlights
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
"""
Find asymmetric vertices
"""
bl_idname = "mesh.find_asymmetric"
bl_label = "Find Asymmetric"
bl_options = {'UNDO', 'REGISTER'}
@classmethod
def poll(cls, context):
object = context.object
if object:
return object.mode == 'EDIT' and object.type == 'MESH'
return False
def execute(self, context):
threshold = 1e-6
object = context.object
bm = bmesh.from_edit_mesh(object.data)
# Deselect all the vertices
for v in bm.verts:
v.select = False
for v1 in bm.verts:
if abs(v1.co[0]) < threshold:
continue
mirror_found = False
for v2 in bm.verts:
if v1 == v2:
continue
if v1.co[0] * v2.co[0] > 0.0:
continue
mirror_coord = Vector(v2.co)
mirror_coord[0] *= -1
if (mirror_coord - v1.co).length_squared < threshold:
mirror_found = True
break
if not mirror_found:
v1.select = True
bm.select_flush_mode()
bmesh.update_edit_mesh(object.data)
return {'FINISHED'}