exclude layer buttons

1.3.5

- added: button to exclude viewlayers and nodes by selection or by hided layers
main
pullusb 2023-06-08 12:39:39 +02:00
parent bc5a046bec
commit 352027ad8c
7 changed files with 134 additions and 31 deletions

View File

@ -14,6 +14,10 @@ Activate / deactivate layer opacity according to prefix
Activate / deactivate all masks using MA layers
-->
1.3.5
- added: button to exclude viewlayers and nodes by selection or by hided layers
1.3.4
- added: multi object merge

View File

@ -26,7 +26,8 @@ class GPEXP_OT_mute_toggle_output_nodes(bpy.types.Operator):
class GPEXP_OT_number_outputs(bpy.types.Operator):
bl_idname = "gp.number_outputs"
bl_label = "Number Outputs"
bl_description = "(Re)Number the outputs to have ordered file by name in export directories\nCtrl+Clic : Delete numbering"
bl_description = "(Re)Number the outputs to have ordered file by name in export directories\
\nCtrl+Clic : Delete numbering"
bl_options = {"REGISTER"}
@classmethod

View File

@ -274,6 +274,85 @@ class GPEXP_OT_merge_viewlayers_to_active(bpy.types.Operator):
self.report(*ret)
return {"FINISHED"}
class GPEXP_OT_remove_viewlayer_on_selected(bpy.types.Operator):
bl_idname = "gp.remove_viewlayer_on_selected"
bl_label = "Exclude Viewlayer"
bl_description = "Set exclude view layers on selected gp layers\
\nRemove associated nodes in Render scene nodetree\
\nCtrl + Click : Affect selected GP objects, not only active"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
# multi_object : bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'})
remove_all_hidden : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
@classmethod
def description(cls, context, properties) -> str:
if properties.remove_all_hidden:
return "Set HIDDEN gp layers to 'exclude' viewlayers\
\nremoving associated nodes in Render scene nodetree\
\nCtrl + Click : Affect selected GP objects, else only active"
else:
return "Set SELECTED gp layers to 'exclude' viewlayers\
\nremoving associated nodes in Render scene nodetree\
\nCtrl + Click : Affect selected GP objects, else only active"
def invoke(self, context, event):
self.multi_object = event.ctrl
return self.execute(context)
def execute(self, context):
ob = context.object
## Force use of render scene (?)
rd_scn = bpy.data.scenes.get('Render')
if not rd_scn:
self.report({'ERROR'}, 'No render scene found')
return {'CANCELLED'}
if self.remove_all_hidden:
if self.multi_object:
layers = [l for ob in context.selected_objects if ob.type == 'GPENCIL' for l in ob.data.layers if l.hide]
else:
layers = [l for l in ob.data.layers if l.select]
else:
if self.multi_object:
layers = [l for ob in context.selected_objects if ob.type == 'GPENCIL' for l in ob.data.layers if l.select]
else:
layers = [l for l in ob.data.layers if l.select]
if not layers:
self.report({'ERROR'}, 'Some layers need to be selected to exclude render viewlayer')
return {'CANCELLED'}
layers = list(set(layers))
## Prepare report / prints in console
exclude_message = ['Layer list set to exclude:']
print('\nLayer list to exclude:')
for l in layers:
vl_name = l.viewlayer_render if l.viewlayer_render else 'None'
mess = f'{l.id_data.name}: {l.info} (previous: {vl_name})'
print(mess)
exclude_message.append(mess)
view_layers = [rd_scn.view_layers.get(l.viewlayer_render) for l in layers\
if l.viewlayer_render and rd_scn.view_layers.get(l.viewlayer_render)]
## remove nodes associated with those viewlayers
fn.remove_nodes_by_viewlayer(view_layers, scene=rd_scn)
## Set selected those layer viewlayer exclude
for l in layers:
l.viewlayer_render = fn.get_view_layer('exclude').name
fn.show_message_box(exclude_message)
return {"FINISHED"}
class GPEXP_OT_merge_preview_ouput(bpy.types.Operator):
bl_idname = "gp.merge_preview_ouput"
bl_label = "Merge Preview Output"
@ -429,6 +508,7 @@ GPEXP_OT_merge_viewlayers_to_active,
GPEXP_OT_auto_merge_adjacent_prefix,
GPEXP_OT_merge_selected_dopesheet_layers,# unused
GPEXP_OT_merge_selected_viewlayer_nodes,
GPEXP_OT_remove_viewlayer_on_selected,
GPEXP_OT_merge_preview_ouput,
)

View File

@ -2,7 +2,7 @@ bl_info = {
"name": "GP Render",
"description": "Organise export of gp layers through compositor output",
"author": "Samuel Bernou",
"version": (1, 3, 4),
"version": (1, 3, 5),
"blender": (2, 93, 0),
"location": "View3D",
"warning": "",

71
fn.py
View File

@ -410,32 +410,18 @@ def get_frames_bbox(node_tree):
return frames_bbox
## -- nodes helper functions
def merge_gplayer_viewlayers(ob=None, act=None, layers=None):
'''ob is not needed if active and layers are passed'''
if ob is None:
ob = bpy.context.object
if act is None:
act = ob.data.layers.active
if layers is None:
layers = [l for l in ob.data.layers if l.select and l != act]
def remove_nodes_by_viewlayer(viewlayer_list, scene=None):
'''Take a list of viewlayer and optionaly a scene to target nodetree
remove nodes related to this viewlayer in nodetree
'''
rd_scn = bpy.data.scenes.get('Render')
if not rd_scn:
return ({'ERROR'}, 'Viewlayers needs to be generated first!')
scene = scene or bpy.context.scene
if not act.viewlayer_render:
return ({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
vl_names = [v.name for v in viewlayer_list]
# list layers and viewlayers
vls = [rd_scn.view_layers.get(l.viewlayer_render) for l in layers
if l.viewlayer_render and l.viewlayer_render != act.viewlayer_render and rd_scn.view_layers.get(l.viewlayer_render)]
vl_names = [v.name for v in vls]
for n in reversed(rd_scn.node_tree.nodes):
for n in reversed(scene.node_tree.nodes):
if n.type == 'R_LAYERS' and n.layer in vl_names:
for lnk in n.outputs[0].links:
grp = lnk.to_node
@ -468,20 +454,49 @@ def merge_gplayer_viewlayers(ob=None, act=None, layers=None):
ngroup.outputs.remove(ngroup.outputs[i])
break
# remove render_layer node
rd_scn.node_tree.nodes.remove(n)
# Remove render_layer node
scene.node_tree.nodes.remove(n)
# assign view layer from active to selected
def merge_gplayer_viewlayers(ob=None, act=None, layers=None):
'''ob is not needed if active and layers are passed'''
if ob is None:
ob = bpy.context.object
if act is None:
act = ob.data.layers.active
if layers is None:
layers = [l for l in ob.data.layers if l.select and l != act]
if act is None:
return ({'ERROR'}, 'Active layer not found. Should be active layer on active object!')
rd_scn = bpy.data.scenes.get('Render')
if not rd_scn:
return ({'ERROR'}, 'Viewlayers needs to be generated first!')
if not act.viewlayer_render:
return ({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
# list layers and viewlayers
vls = [rd_scn.view_layers.get(l.viewlayer_render) for l in layers
if l.viewlayer_render and l.viewlayer_render != act.viewlayer_render and rd_scn.view_layers.get(l.viewlayer_render)]
# Remove viewlayer related nodes
remove_nodes_by_viewlayer(vls, rd_scn)
# Assign view layer from active to selected
for l in layers:
l.viewlayer_render = act.viewlayer_render
## delete unused_vl
## Delete unused viewlayers ()
# used_vl_name = [n.layer for n in rd_scn.node_tree.nodes if n.type == 'R_LAYERS' and n.layer]
used_vl_name = [n.layer for n in rd_scn.node_tree.nodes if n.type == 'R_LAYERS' and n.layer]
for vl in vls:
rd_scn.view_layers.remove(vl)
# if not vl.name in used_vl_name:
# rd_scn.view_layers.remove(vl)
if vl.name == 'exclude':
# keep exclude
continue
if not vl.name in used_vl_name:
rd_scn.view_layers.remove(vl)
def group_adjacent_layer_prefix_rlayer(ob, excluded_prefix=[], first_name=True):
'''Set viewlayer and renderlayers by Gp layer adjacent prefix

3
ui.py
View File

@ -213,6 +213,9 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
row.enabled= ct > 1
col.operator('gpexp.auto_merge_adjacent_prefix', icon='SELECT_EXTEND')
row = col.row(align=True)
row.operator('gp.remove_viewlayer_on_selected', text=f'Exclude {ct} layers', icon='X').remove_all_hidden = False
row.operator('gp.remove_viewlayer_on_selected', text='', icon='HIDE_ON').remove_all_hidden = True
## all and objects
layout.separator()