partial autobuild

1.1.0

- added: `autobuild` button (partial auto-buildfor now)
- added: make sent object selected
main
pullusb 2023-01-05 18:05:24 +01:00
parent e086eb4050
commit 10c279c315
6 changed files with 102 additions and 69 deletions

View File

@ -2,7 +2,7 @@
<!-- TODO
- need to let use choose output settings
- need to let user choose output settings
if objects has multiple user without being linked in render scene :
duplicate the object insteat of linking
@ -14,6 +14,11 @@ Activate / deactivate layer opacity according to prefix
Activate / deactivate all masks using MA layers
-->
1.1.0
- added: `autobuild` button (partial auto-buildfor now)
- added: make sent object selected
1.0.3
- fixed: Send to render layer compatibility with blender 3.4

View File

@ -75,18 +75,20 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
if (render_wkspace := bpy.data.workspaces.get('GP Render')):
context.window.workspace = render_wkspace
else:
render_wkspace_filepath = Path(bpy.utils.user_resource('SCRIPTS'), 'startup', 'GP', 'startup.blend')
render_wkspace_filepath = Path(bpy.utils.user_resource('SCRIPTS'), 'startup', 'bl_app_templates_user', 'GP', 'startup.blend')
print('render workspace', render_wkspace_filepath.exists())
ret = bpy.ops.workspace.append_activate(idname='GP Render', filepath=str(render_wkspace_filepath))
print('ret: ', ret)
if ret != {'FINISHED'}:
# Fallback to workspace template shipped with addon (TODO : add template blend file in addon)
render_wkspace_filepath = Path(__file__).parent / 'workspaces' / 'startup.blend'
ret = bpy.ops.workspace.append_activate(idname='GP Render', filepath=str(render_wkspace_filepath))
if ret != {'FINISHED'}:
print('No GP render workspace available')
## Group all adjacent layer type
## Renumber File outputs
## Trigger check file before finishing ?

View File

@ -114,6 +114,73 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
return ng, out
def merge_gplayer_viewlayers(ob, act=None, layers=None):
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]
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)]
vl_names = [v.name for v in vls]
for n in reversed(rd_scn.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
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)
# remove input and output from group
# grp.inputs.remove(sockin) # do not clear inside !!
# grp.outputs.remove(sockout) # do not clear inside !!
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])
break
for i in range(len(grp.outputs))[::-1]:
if grp.outputs[i].name == sockout.name:
ngroup.outputs.remove(ngroup.outputs[i])
break
# remove render_layer node
rd_scn.node_tree.nodes.remove(n)
# assign view layer from active to selected
for l in layers:
l.viewlayer_render = act.viewlayer_render
## delete unused_vl
# 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)
class GPEXP_OT_merge_viewlayers_to_active(bpy.types.Operator):
bl_idname = "gp.merge_viewlayers_to_active"
bl_label = "Merge selected layers view_layers"
@ -130,69 +197,19 @@ class GPEXP_OT_merge_viewlayers_to_active(bpy.types.Operator):
act = ob.data.layers.active
layers = [l for l in ob.data.layers if l.select and l != act]
if not act.viewlayer_render:
self.report({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
return {'CANCELLED'}
## Tested in func
# rd_scn = bpy.data.scenes.get('Render')
# if not rd_scn:
# self.report({'ERROR'}, 'Viewlayers needs to be generated first!')
# return {'CANCELLED'}
rd_scn = bpy.data.scenes.get('Render')
if not rd_scn:
self.report({'ERROR'}, 'Viewlayers needs to be generated first!')
return {'CANCELLED'}
# 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):
if n.type == 'R_LAYERS' and n.layer in vl_names:
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)
# remove input and output from group
# grp.inputs.remove(sockin) # do not clear inside !!
# grp.outputs.remove(sockout) # do not clear inside !!
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])
break
for i in range(len(grp.outputs))[::-1]:
if grp.outputs[i].name == sockout.name:
ngroup.outputs.remove(ngroup.outputs[i])
break
# remove render_layer node
rd_scn.node_tree.nodes.remove(n)
# assign view layer from active to selected
for l in layers:
l.viewlayer_render = act.viewlayer_render
## delete unused_vl
# 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 not act.viewlayer_render:
# self.report({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
# return {'CANCELLED'}
ret = merge_gplayer_viewlayers(ob, act=act, layers=layers)
if isinstance(ret, tuple):
self.report(*ret)
return {"FINISHED"}
class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):

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, 0, 2),
"version": (1, 1, 0),
"blender": (2, 93, 0),
"location": "View3D",
"warning": "",

View File

@ -235,6 +235,16 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
scene.collection.objects.link(ob)
ob.hide_viewport = ob.hide_render = False
## set object active in default viewlayer
# if (avl := scene.view_layers.get('ViewLayer')):
# # This select the object in source scene
# avl.objects.active = ob
# # avl.objects.active.select_set(True)
nob = scene.collection.objects.get(ob.name)
if nob:
nob.select_set(True)
# create viewlayer
vl_name = f'{ob.name} / {l.info}'
vl = fn.get_view_layer(vl_name, scene=scene)

3
ui.py
View File

@ -174,8 +174,7 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
def draw(self, context):
layout = self.layout
## TODO: add auto-build
# layout.operator('gp_export.render_auto_build')
layout.operator('gp_export.render_auto_build')
if context.object:
layout.label(text=f'Object: {context.object.name}')
if context.object.data.users > 1: