viewlayer management and fixes
0.3.7 - fix: set render scene res at 100% at creation - fix: exclude VL assignation - feat: delete a render_layer (and add concerned gp layers to exclude) - feat: reactivate all viewlayers - feat: activate only selected viewlayer for fast re-render - ui: rearrange + new buttonsmain
parent
e7621be9a8
commit
a0a1647bf9
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -14,23 +14,32 @@ Activate / deactivate layer opaticty according to prefix
|
|||
Activate / deactivate all masks using MA layers
|
||||
-->
|
||||
|
||||
0.3.7
|
||||
|
||||
- fix: set render scene res at 100% at creation
|
||||
- fix: exclude VL assignation
|
||||
- feat: delete a render_layer (and add concerned gp layers to exclude)
|
||||
- feat: reactivate all viewlayers
|
||||
- feat: activate only selected viewlayer for fast re-render
|
||||
- ui: rearrange + new buttons
|
||||
|
||||
0.3.6
|
||||
|
||||
change: output settings switch from PNG to EXR
|
||||
fix: set render scene output (preview) to jpeg fast to write
|
||||
fix: correct copy output format ops
|
||||
- change: output settings switch from PNG to EXR
|
||||
- fix: set render scene output (preview) to jpeg fast to write
|
||||
- fix: correct copy output format ops
|
||||
|
||||
0.3.5:
|
||||
|
||||
feat: set full opacity -> skip chosen prefix (MA by default)
|
||||
- feat: set full opacity -> skip chosen prefix (MA by default)
|
||||
|
||||
0.3.4:
|
||||
|
||||
feat: swap cams button, code copied from `bg_plane_manager`
|
||||
- feat: swap cams button, code copied from `bg_plane_manager`
|
||||
|
||||
0.3.3:
|
||||
|
||||
fix: norm name : lowercase first (else bad naming break prefix)
|
||||
- fix: norm name : lowercase first (else bad naming break prefix)
|
||||
|
||||
0.3.2
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
|||
for l in ob.data.layers:
|
||||
if not l.select:
|
||||
if not l.viewlayer_render:
|
||||
# TODO : need to link, can reaise error if object is not linked in Render scene yet
|
||||
l.viewlayer_render == fn.get_view_layer('exclude').name
|
||||
# TODO : need to link, can raise error if object is not linked in Render scene yet
|
||||
l.viewlayer_render = fn.get_view_layer('exclude').name
|
||||
continue
|
||||
gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||
|
||||
|
|
|
@ -56,8 +56,88 @@ class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
|
|||
|
||||
return {"FINISHED"}
|
||||
|
||||
class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
||||
bl_idname = "gp.delete_render_layer"
|
||||
bl_label = "Delete Render Layer"
|
||||
bl_description = "Delete selected render layers"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
# mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
rd_scn = bpy.data.scenes.get('Render')
|
||||
if not rd_scn:
|
||||
self.report({'ERROR'}, 'Viewlayers needs to be generated first!')
|
||||
return {'CANCELLED'}
|
||||
|
||||
nodes = rd_scn.node_tree.nodes
|
||||
# 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)]
|
||||
|
||||
rlayers_nodes = [n for n in nodes if n.select and n.type == 'R_LAYERS']
|
||||
|
||||
vls = [rd_scn.view_layers.get(n.layer) for n in rlayers_nodes if rd_scn.view_layers.get(n.layer)]
|
||||
|
||||
vl_names = [v.name for v in vls]
|
||||
## disable layers using those VL
|
||||
for ob in [o for o in rd_scn.objects if o.type == 'GPENCIL']:
|
||||
for l in ob.data.layers:
|
||||
if l.viewlayer_render in vl_names:
|
||||
l.viewlayer_render = fn.get_view_layer('exclude').name
|
||||
|
||||
for n in reversed(rlayers_nodes):
|
||||
# Disconnect linked
|
||||
for lnk in n.outputs[0].links:
|
||||
grp = lnk.to_node
|
||||
if grp.type != 'GROUP':
|
||||
continue
|
||||
if not grp.name.startswith('NG'):
|
||||
continue
|
||||
sockin = lnk.to_socket
|
||||
sockout = grp.outputs.get(sockin.name)
|
||||
if not sockout:
|
||||
continue
|
||||
for grplink in sockout.links:
|
||||
if grplink.to_node.type != 'OUTPUT_FILE':
|
||||
continue
|
||||
fo_socket = grplink.to_socket
|
||||
fo = grplink.to_node
|
||||
fo.file_slots.remove(fo_socket)
|
||||
|
||||
inside_nodes = []
|
||||
ngroup = grp.node_tree
|
||||
for i in range(len(grp.inputs))[::-1]:
|
||||
if grp.inputs[i].name == sockin.name:
|
||||
ngroup.inputs.remove(ngroup.inputs[i])
|
||||
|
||||
gp_in_socket = ngroup.nodes['Group Input'].outputs[i]
|
||||
for lnk in gp_in_socket.links:
|
||||
inside_nodes += fn.all_connected_forward(lnk.to_node)
|
||||
list(set(inside_nodes))
|
||||
break
|
||||
for i in range(len(grp.outputs))[::-1]:
|
||||
if grp.outputs[i].name == sockout.name:
|
||||
ngroup.outputs.remove(ngroup.outputs[i])
|
||||
break
|
||||
|
||||
for sub_n in reversed(inside_nodes):
|
||||
ngroup.nodes.remove(sub_n)
|
||||
|
||||
# remove render_layer node
|
||||
rd_scn.node_tree.nodes.remove(n)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes=(
|
||||
GPEXP_OT_reconnect_render_layer,
|
||||
GPEXP_OT_delete_render_layer,
|
||||
)
|
||||
|
||||
def register():
|
||||
|
|
|
@ -167,11 +167,54 @@ class GPEXP_OT_normalize_outnames(bpy.types.Operator):
|
|||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GPEXP_OT_enable_all_viewlayers(bpy.types.Operator):
|
||||
bl_idname = "gp.enable_all_viewlayers"
|
||||
bl_label = "Enable All Viewlayers"
|
||||
bl_description = "Enable all View layers except those named 'exclude' 'View Layer'"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
def execute(self, context):
|
||||
rd_scn = bpy.data.scenes.get('Render')
|
||||
if not rd_scn:
|
||||
print('SKIP, no Render scene')
|
||||
return {"CANCELLED"}
|
||||
|
||||
vl_list = [vl for vl in rd_scn.view_layers if not vl.use and vl.name not in {'View Layer', 'exclude'}]
|
||||
for v in vl_list:
|
||||
v.use = True
|
||||
|
||||
self.report({"INFO"}, f'{len(vl_list)} ViewLayers Reactivated')
|
||||
return {"FINISHED"}
|
||||
|
||||
class GPEXP_OT_activate_only_selected_layers(bpy.types.Operator):
|
||||
bl_idname = "gp.activate_only_selected_layers"
|
||||
bl_label = "Activate Only Selected Layers"
|
||||
bl_description = "Activate only selected node view layer , excluding all others"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
def execute(self, context):
|
||||
rd_scn = bpy.data.scenes.get('Render')
|
||||
if not rd_scn:
|
||||
print('SKIP, no Render scene')
|
||||
return {"CANCELLED"}
|
||||
|
||||
nodes = rd_scn.node_tree.nodes
|
||||
|
||||
rlayers_nodes = [n for n in nodes if n.select and n.type == 'R_LAYERS']
|
||||
vls = [rd_scn.view_layers.get(n.layer) for n in rlayers_nodes if rd_scn.view_layers.get(n.layer)]
|
||||
for v in rd_scn.view_layers:
|
||||
v.use = v in vls
|
||||
|
||||
self.report({"INFO"}, f'Now only {len(vls)} viewlayer active (/{len(rd_scn.view_layers)})')
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes=(
|
||||
GPEXP_OT_mute_toggle_output_nodes,
|
||||
GPEXP_OT_set_output_node_format,
|
||||
GPEXP_OT_number_outputs,
|
||||
GPEXP_OT_enable_all_viewlayers,
|
||||
GPEXP_OT_activate_only_selected_layers,
|
||||
# GPEXP_OT_normalize_outnames,
|
||||
)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
|||
"name": "GP Render",
|
||||
"description": "Organise export of gp layers through compositor output",
|
||||
"author": "Samuel Bernou",
|
||||
"version": (0, 3, 6),
|
||||
"version": (0, 3, 7),
|
||||
"blender": (2, 93, 0),
|
||||
"location": "View3D",
|
||||
"warning": "",
|
||||
|
|
19
fn.py
19
fn.py
|
@ -114,6 +114,8 @@ def set_settings(scene=None):
|
|||
scene.render.film_transparent = True
|
||||
scene.view_settings.view_transform = 'Standard'
|
||||
|
||||
scene.render.resolution_percentage = 100
|
||||
|
||||
# output (fast write settings since this is just to delete afterwards...)
|
||||
scene.render.filepath = '//render/preview/preview_'
|
||||
scene.render.image_settings.file_format = 'JPEG'
|
||||
|
@ -370,6 +372,23 @@ def connect_to_group_input(n):
|
|||
return val
|
||||
return False
|
||||
|
||||
def all_connected_forward(n, nlist=[]):
|
||||
'''return list of all forward connected nodes recursively (include passed nodes)'''
|
||||
for o in n.outputs:
|
||||
if o.is_linked:
|
||||
for lnk in o.links:
|
||||
if lnk.to_node.type == 'GROUP_OUTPUT':
|
||||
if n not in nlist:
|
||||
return nlist + [n]
|
||||
else:
|
||||
return nlist
|
||||
else:
|
||||
nlist = all_connected_forward(lnk.to_node, nlist)
|
||||
if n in nlist:
|
||||
return nlist
|
||||
return nlist + [n]
|
||||
|
||||
|
||||
def clear_nodegroup_content_if_disconnected(ngroup):
|
||||
'''Get a nodegroup.node_tree
|
||||
delete orphan nodes that are not connected from group input node
|
||||
|
|
43
ui.py
43
ui.py
|
@ -2,7 +2,6 @@ import bpy
|
|||
from bpy.types import Panel
|
||||
# from .preferences import get_addon_prefs
|
||||
|
||||
|
||||
# Node view panel
|
||||
class GPEXP_PT_gp_node_ui(Panel):
|
||||
bl_space_type = "NODE_EDITOR"
|
||||
|
@ -28,19 +27,51 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
# if cam and cam_name == 'draw_cam':
|
||||
# cam_name = f'{cam.parent.name} > {cam_name}'
|
||||
row.operator("gp.swap_render_cams", text=text, icon='OUTLINER_OB_CAMERA')
|
||||
|
||||
# Live checks
|
||||
if scn.render.resolution_percentage != 100:
|
||||
layout.label(text='Res Percentage not 100%', icon='ERROR')
|
||||
layout.prop(scn.render, 'resolution_percentage')
|
||||
|
||||
exclude_count = len([vl for vl in scn.view_layers if not vl.use and vl.name not in {'View Layer', 'exclude'}])
|
||||
if exclude_count:
|
||||
# layout.label(text=f'{exclude_count} Excluded View Layers !')
|
||||
layout.operator('gp.enable_all_viewlayers', text=f'Reactivate {exclude_count} Excluded View Layers')
|
||||
|
||||
if not scn.use_nodes or not scn.node_tree:
|
||||
return
|
||||
|
||||
layout.separator()
|
||||
# TODO : add advanced bool checkbox to hide some options from the user
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
||||
layout.label(text='View layers:')
|
||||
ct = len([n for n in context.scene.node_tree.nodes if n.type == 'R_LAYERS' and n.select])
|
||||
col = layout.column(align=True)
|
||||
|
||||
col.operator('gp.activate_only_selected_layers', text=f'Activate Only {ct} Layer Nodes')
|
||||
col.enabled = ct > 0
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
||||
txt = f'Merge {ct} Layer Nodes'
|
||||
col.operator('gp.merge_selected_viewlayer_nodes', icon='NODETREE', text=txt).disconnect = True
|
||||
col.operator('gp.merge_selected_viewlayer_nodes', icon='NODETREE', text='Merge (keep connect)').disconnect = False
|
||||
col.enabled = ct > 1
|
||||
|
||||
layout.separator()
|
||||
col = layout.column()
|
||||
subcol = col.column()
|
||||
n = context.scene.node_tree.nodes.active
|
||||
if n:
|
||||
subcol.enabled = n and n.type == 'R_LAYERS' and not n.outputs[0].is_linked
|
||||
else:
|
||||
subcol.enabled = False
|
||||
|
||||
subcol.operator('gp.reconnect_render_layer', icon='ANIM', text=f'Reconnect {ct} Layer Node')
|
||||
|
||||
col.operator('gp.delete_render_layer', icon='TRACKING_CLEAR_FORWARDS', text=f'Delete {ct} Layer Node')
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.label(text='All Outputs:')
|
||||
|
@ -53,14 +84,8 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
col=layout.column()
|
||||
col.label(text='Clean and updates:')
|
||||
|
||||
row = col.row()
|
||||
n = context.scene.node_tree.nodes.active
|
||||
if n:
|
||||
row.enabled = n and n.type == 'R_LAYERS' and not n.outputs[0].is_linked
|
||||
else:
|
||||
row.enabled = False
|
||||
|
||||
row.operator('gp.reconnect_render_layer', icon='ANIM', text='Reconnect')
|
||||
col.separator()
|
||||
|
||||
col.operator('gp.clean_compo_tree', icon='BRUSHES_ALL', text='Clean Nodes') # NODE_CORNER
|
||||
|
||||
|
|
Loading…
Reference in New Issue