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
|
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
|
0.3.6
|
||||||
|
|
||||||
change: output settings switch from PNG to EXR
|
- change: output settings switch from PNG to EXR
|
||||||
fix: set render scene output (preview) to jpeg fast to write
|
- fix: set render scene output (preview) to jpeg fast to write
|
||||||
fix: correct copy output format ops
|
- fix: correct copy output format ops
|
||||||
|
|
||||||
0.3.5:
|
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:
|
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:
|
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
|
0.3.2
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
||||||
for l in ob.data.layers:
|
for l in ob.data.layers:
|
||||||
if not l.select:
|
if not l.select:
|
||||||
if not l.viewlayer_render:
|
if not l.viewlayer_render:
|
||||||
# TODO : need to link, can reaise error if object is not linked in Render scene yet
|
# 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
|
l.viewlayer_render = fn.get_view_layer('exclude').name
|
||||||
continue
|
continue
|
||||||
gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,88 @@ class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
|
||||||
|
|
||||||
return {"FINISHED"}
|
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=(
|
classes=(
|
||||||
GPEXP_OT_reconnect_render_layer,
|
GPEXP_OT_reconnect_render_layer,
|
||||||
|
GPEXP_OT_delete_render_layer,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|
|
@ -167,11 +167,54 @@ class GPEXP_OT_normalize_outnames(bpy.types.Operator):
|
||||||
return {"FINISHED"}
|
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=(
|
classes=(
|
||||||
GPEXP_OT_mute_toggle_output_nodes,
|
GPEXP_OT_mute_toggle_output_nodes,
|
||||||
GPEXP_OT_set_output_node_format,
|
GPEXP_OT_set_output_node_format,
|
||||||
GPEXP_OT_number_outputs,
|
GPEXP_OT_number_outputs,
|
||||||
|
GPEXP_OT_enable_all_viewlayers,
|
||||||
|
GPEXP_OT_activate_only_selected_layers,
|
||||||
# GPEXP_OT_normalize_outnames,
|
# GPEXP_OT_normalize_outnames,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
||||||
"name": "GP Render",
|
"name": "GP Render",
|
||||||
"description": "Organise export of gp layers through compositor output",
|
"description": "Organise export of gp layers through compositor output",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (0, 3, 6),
|
"version": (0, 3, 7),
|
||||||
"blender": (2, 93, 0),
|
"blender": (2, 93, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
|
19
fn.py
19
fn.py
|
@ -114,6 +114,8 @@ def set_settings(scene=None):
|
||||||
scene.render.film_transparent = True
|
scene.render.film_transparent = True
|
||||||
scene.view_settings.view_transform = 'Standard'
|
scene.view_settings.view_transform = 'Standard'
|
||||||
|
|
||||||
|
scene.render.resolution_percentage = 100
|
||||||
|
|
||||||
# output (fast write settings since this is just to delete afterwards...)
|
# output (fast write settings since this is just to delete afterwards...)
|
||||||
scene.render.filepath = '//render/preview/preview_'
|
scene.render.filepath = '//render/preview/preview_'
|
||||||
scene.render.image_settings.file_format = 'JPEG'
|
scene.render.image_settings.file_format = 'JPEG'
|
||||||
|
@ -370,6 +372,23 @@ def connect_to_group_input(n):
|
||||||
return val
|
return val
|
||||||
return False
|
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):
|
def clear_nodegroup_content_if_disconnected(ngroup):
|
||||||
'''Get a nodegroup.node_tree
|
'''Get a nodegroup.node_tree
|
||||||
delete orphan nodes that are not connected from group input node
|
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 bpy.types import Panel
|
||||||
# from .preferences import get_addon_prefs
|
# from .preferences import get_addon_prefs
|
||||||
|
|
||||||
|
|
||||||
# Node view panel
|
# Node view panel
|
||||||
class GPEXP_PT_gp_node_ui(Panel):
|
class GPEXP_PT_gp_node_ui(Panel):
|
||||||
bl_space_type = "NODE_EDITOR"
|
bl_space_type = "NODE_EDITOR"
|
||||||
|
@ -28,19 +27,51 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
# if cam and cam_name == 'draw_cam':
|
# if cam and cam_name == 'draw_cam':
|
||||||
# cam_name = f'{cam.parent.name} > {cam_name}'
|
# cam_name = f'{cam.parent.name} > {cam_name}'
|
||||||
row.operator("gp.swap_render_cams", text=text, icon='OUTLINER_OB_CAMERA')
|
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:
|
if not scn.use_nodes or not scn.node_tree:
|
||||||
return
|
return
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
# TODO : add advanced bool checkbox to hide some options from the user
|
# 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])
|
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'
|
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=txt).disconnect = True
|
||||||
col.operator('gp.merge_selected_viewlayer_nodes', icon='NODETREE', text='Merge (keep connect)').disconnect = False
|
col.operator('gp.merge_selected_viewlayer_nodes', icon='NODETREE', text='Merge (keep connect)').disconnect = False
|
||||||
col.enabled = ct > 1
|
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.separator()
|
||||||
|
|
||||||
layout.label(text='All Outputs:')
|
layout.label(text='All Outputs:')
|
||||||
|
@ -53,14 +84,8 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
col=layout.column()
|
col=layout.column()
|
||||||
col.label(text='Clean and updates:')
|
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
|
col.operator('gp.clean_compo_tree', icon='BRUSHES_ALL', text='Clean Nodes') # NODE_CORNER
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue