autobuild beta

1.1.1

- changed: autobuild beta
main
pullusb 2023-01-06 15:10:10 +01:00
parent 10c279c315
commit a736eec7c9
8 changed files with 288 additions and 133 deletions

View File

@ -14,6 +14,10 @@ Activate / deactivate layer opacity according to prefix
Activate / deactivate all masks using MA layers Activate / deactivate all masks using MA layers
--> -->
1.1.1
- changed: autobuild beta
1.1.0 1.1.0
- added: `autobuild` button (partial auto-buildfor now) - added: `autobuild` button (partial auto-buildfor now)

View File

@ -43,23 +43,6 @@ class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
def export_gp_objects(oblist, exclude_list=[], scene=None):
# Skip layer containing element in excluyde list
if not isinstance(oblist, list):
oblist = [oblist]
for ob in oblist:
for l in ob.data.layers:
# if l.hide:
# continue
if l.hide or any(x + '_' in l.info for x in exclude_list): # exclude hided ?
l.viewlayer_render = fn.get_view_layer('exclude', scene=scene).name # assign "exclude"
continue
_vl, _cp = gen_vlayer.get_set_viewlayer_from_gp(ob, l, scene=scene) # scene=fn.get_render_scene())
## send operator with mode ALL or SELECTED to batch build ## send operator with mode ALL or SELECTED to batch build
class GPEXP_OT_add_objects_to_render(bpy.types.Operator): class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
bl_idname = "gp.add_object_to_render" bl_idname = "gp.add_object_to_render"
@ -84,14 +67,14 @@ class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
excludes = [] # ['MA', 'IN'] # Get list dynamically excludes = [] # ['MA', 'IN'] # Get list dynamically
if self.mode == 'SELECTED': if self.mode == 'SELECTED':
export_gp_objects([o for o in context.selected_objects if o.type == 'GPENCIL'], exclude_list=excludes, scene=scn) gen_vlayer.export_gp_objects([o for o in context.selected_objects if o.type == 'GPENCIL'], exclude_list=excludes, scene=scn)
elif self.mode == 'ALL': elif self.mode == 'ALL':
# scn = bpy.data.scenes.get('Scene') # scn = bpy.data.scenes.get('Scene')
# if not scn: # if not scn:
# self.report({'ERROR'}, 'Could not found default scene') # self.report({'ERROR'}, 'Could not found default scene')
# return {"CANCELLED"} # return {"CANCELLED"}
export_gp_objects([o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)], exclude_list=excludes, scene=scn) gen_vlayer.export_gp_objects([o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)], exclude_list=excludes, scene=scn)
return {"FINISHED"} return {"FINISHED"}

View File

@ -2,6 +2,48 @@ import bpy
from pathlib import Path from pathlib import Path
from . import gen_vlayer, fn from . import gen_vlayer, fn
def batch_setup_render_scene(context=None):
'''A series of setup actions for Render scene:
- renumber fileout
- Clean compo Tree
- Go to camera view in visible viewports
- Swap to bg cam
'''
if context is None:
context = bpy.context
render_scn = context.scene
## Renumber File outputs
print('Renumber File outputs')
for fo in render_scn.node_tree.nodes:
if fo.type == 'OUTPUT_FILE':
fn.renumber_keep_existing(fo)
## Swap to bg_cam (if any)
if render_scn.objects.get('bg_cam') and (not render_scn.camera or render_scn.camera.name != 'bg_cam'):
print('Swap to bg cam')
bpy.ops.gp.swap_render_cams()
## Go to camera view in visible viewports
print('Go to camera view in visible viewports')
if render_scn.camera:
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'VIEW_3D':
area.spaces.active.region_3d.view_perspective = 'CAMERA'
## Clean compo Tree
print('Clean compo Tree')
bpy.ops.gp.clean_compo_tree('EXEC_DEFAULT', use_render_scene=True)
# bpy.ops.gp.clean_compo_tree('INVOKE_DEFAULT', use_render_scene=True)
## Trigger check file before finishing ?
# bpy.ops.gp.check_render_scene('INVOKE_DEFAULT')
class GPEXP_OT_render_auto_build(bpy.types.Operator): class GPEXP_OT_render_auto_build(bpy.types.Operator):
bl_idname = "gp_export.render_auto_build" bl_idname = "gp_export.render_auto_build"
bl_label = "Auto-Build" bl_label = "Auto-Build"
@ -12,58 +54,51 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
def poll(cls, context): def poll(cls, context):
return context.object and context.object.type == 'GPENCIL' return context.object and context.object.type == 'GPENCIL'
excluded_prefix : bpy.props.StringProperty(
name='Excluded Layer By Prefix', default='GP,RG,PO',
description='Exclude layer to send to render by prefix (comma separated list)')
## TODO: add props to fine tune the auto-build
# mode : bpy.props.StringProperty(options={'SKIP_SAVE'}) # mode : bpy.props.StringProperty(options={'SKIP_SAVE'})
def execute(self, context): def execute(self, context):
''' print('-- Auto-build Render scene --\n')
ob = context.object
layer = ob.data.layers.active
if not layer:
self.report({'ERROR'}, 'No active layer')
return {"CANCELLED"}
ct = 0
# send scene ?
hided = 0
for l in ob.data.layers:
if not l.select:
if not l.viewlayer_render:
l.viewlayer_render = fn.get_view_layer('exclude').name
continue
gen_vlayer.get_set_viewlayer_from_gp(ob, l)
if l.hide:
hided += 1
ct += 1
if hided:
self.report({'WARNING'}, f'{hided}/{ct} layers are hided !')
else:
self.report({'INFO'}, f'{ct} layer(s) added to scene "Render"')
'''
## TODO: add colors to layers (specified in ENV or hardcoded for now...) ## TODO: add colors to layers (specified in ENV or hardcoded for now...)
## Option: Maybe find a way to create a color from prefix hash ? (wlways give unique color with same prefix on other project!) ## Option: Maybe find a way to create a color from prefix hash ? (always give unique color with same prefix on other project!)
render_scn = bpy.data.scenes.get('Render') render_scn = bpy.data.scenes.get('Render')
if render_scn: if render_scn:
self.report({'ERROR'}, 'A "Render" scene already exists') self.report({'ERROR'}, 'A "Render" scene already exists')
return {'CANCELLED'} return {'CANCELLED'}
ob_list = [o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)]
if not ob_list:
self.report({'ERROR'}, 'No GP object to render found')
return {'CANCELLED'}
print('GP objects to send:')
for o in ob_list:
print(f' - {o.name}')
## Trigger rename lowercase ## Trigger rename lowercase
bpy.ops.gp.lower_layers_name() print('Trigger rename lowercase')
bpy.ops.gp.lower_layers_name('EXEC_DEFAULT')
# bpy.ops.gp.lower_layers_name('INVOKE_DEFAULT')
## Trigger renumber by distance ## Trigger renumber by distance
bpy.ops.gp.auto_number_object() print('Trigger renumber by distance')
bpy.ops.gp.auto_number_object('EXEC_DEFAULT')
# bpy.ops.gp.auto_number_object('INVOKE_DEFAULT')
## Export layer infos ? (skip if json already exists) ## Export layer infos ? (skip if json already exists)
print('Export layer infos (skip if json already exists)')
bpy.ops.gp.export_infos_for_compo('INVOKE_DEFAULT', skip_check=True) bpy.ops.gp.export_infos_for_compo('INVOKE_DEFAULT', skip_check=True)
## Send all GP to render scene ## Send all GP to render scene
bpy.ops.gp.add_object_to_render(mode="ALL") print('Send all GP to render scene')
# bpy.ops.gp.add_object_to_render(mode="ALL") # Ops to send all
gen_vlayer.export_gp_objects(ob_list, exclude_list=self.excluded_prefix) # Create render scene OTF
## Switch to new Render Scene ## Switch to new Render Scene
print('Switch to new Render Scene')
render_scn = bpy.data.scenes.get('Render') render_scn = bpy.data.scenes.get('Render')
if not render_scn: if not render_scn:
self.report({'ERROR'}, 'No render scene found') self.report({'ERROR'}, 'No render scene found')
@ -72,11 +107,11 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
## Change to GP workspace (if needed) ## Change to GP workspace (if needed)
if context.window.workspace.name != 'GP Render': if context.window.workspace.name != 'GP Render':
print('Change to GP workspace')
if (render_wkspace := bpy.data.workspaces.get('GP Render')): if (render_wkspace := bpy.data.workspaces.get('GP Render')):
context.window.workspace = render_wkspace context.window.workspace = render_wkspace
else: else:
render_wkspace_filepath = Path(bpy.utils.user_resource('SCRIPTS'), 'startup', 'bl_app_templates_user', '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)) ret = bpy.ops.workspace.append_activate(idname='GP Render', filepath=str(render_wkspace_filepath))
print('ret: ', ret) print('ret: ', ret)
if ret != {'FINISHED'}: if ret != {'FINISHED'}:
@ -87,20 +122,61 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
print('No GP render workspace available') print('No GP render workspace available')
## Group all adjacent layer type ## Group all adjacent layer type
print('Group all adjacent layer type')
for ob in ob_list:
fn.group_adjacent_layer_prefix_rlayer(ob, excluded_prefix=['GP', 'PO', 'RG'], first_name=True)
# bpy.ops.gp_export.render_scene_setup() # next render scene setup at once
## Renumber File outputs ## Renumber File outputs
print('Renumber File outputs')
for fo in render_scn.node_tree.nodes:
if fo.type == 'OUTPUT_FILE':
fn.renumber_keep_existing(fo)
## Swap to bg_cam (if any)
if render_scn.objects.get('bg_cam') and (not render_scn.camera or render_scn.camera.name != 'bg_cam'):
print('Swap to bg cam')
bpy.ops.gp.swap_render_cams()
## Go to camera view in visible viewports
if render_scn.camera:
print('Go to camera view in visible viewports')
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'VIEW_3D':
area.spaces.active.region_3d.view_perspective = 'CAMERA'
## Clean compo Tree
print('Clean compo Tree')
# bpy.ops.gp.clean_compo_tree('INVOKE_DEFAULT', use_render_scene=True)
bpy.ops.gp.clean_compo_tree('EXEC_DEFAULT', use_render_scene=True)
## Trigger check file before finishing ? ## Trigger check file before finishing ?
# bpy.ops.gp.check_render_scene('INVOKE_DEFAULT')
## Note: After all these operation, a ctrl+Z might crash
## note: After all these operation, a ctrl+Z might crash print('\nDone.')
return {"FINISHED"} return {"FINISHED"}
class GPEXP_OT_render_scene_setup(bpy.types.Operator):
bl_idname = "gp_export.render_scene_setup"
bl_label = "Batch Setup Render Scene"
bl_description = "Batch some actions to setup render scene:\
\n- renumber file output nodes\
\n- Clean compo Tree\
\n- Go to camera view in visible viewports\
\n- Swap to bg cam"
bl_options = {"REGISTER"}
def execute(self, context):
print('-- Auto-setup Render scene --\n')
batch_setup_render_scene(context=context)
return {"FINISHED"}
classes=( classes=(
GPEXP_OT_render_auto_build, GPEXP_OT_render_auto_build,
GPEXP_OT_render_scene_setup,
) )
def register(): def register():

View File

@ -50,13 +50,17 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
""" """
class GPEXP_OT_clean_compo_tree(bpy.types.Operator): class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
bl_idname = "gp.clean_compo_tree" bl_idname = "gp.clean_compo_tree"
bl_label = "Clean Compo Tree" bl_label = "Clean Compo Tree"
bl_description = "Pop up menu with cleaning options" bl_description = "Pop up menu with cleaning options"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
# Internal prop (use when launching from python)
use_render_scene : bpy.props.BoolProperty(name="Use Render Scene",
description="Force the clean on scene named Render, abort if not exists (not exposed)",
default=False, options={'SKIP_SAVE'})
clear_unused_view_layers : bpy.props.BoolProperty(name="Clear unused view layers", clear_unused_view_layers : bpy.props.BoolProperty(name="Clear unused view layers",
description="Delete view layer that aren't used in the nodetree anymore", description="Delete view layer that aren't used in the nodetree anymore",
default=True) default=True)
@ -86,7 +90,6 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
return True return True
def invoke(self, context, event): def invoke(self, context, event):
# self.nodes = context.object
return context.window_manager.invoke_props_dialog(self) return context.window_manager.invoke_props_dialog(self)
def draw(self, context): def draw(self, context):
@ -107,12 +110,14 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
# box.prop(self, 'reorder_inputs') # box.prop(self, 'reorder_inputs')
# box.prop(self, 'fo_clear_disconnected') # box.prop(self, 'fo_clear_disconnected')
def execute(self, context): def execute(self, context):
# render = bpy.data.scenes.get('Render') if self.use_render_scene:
# if not render: render = bpy.data.scenes.get('Render')
# print('SKIP, no Render scene') if not render:
# return {"CANCELLED"} print('SKIP clean_compo_tree, No "Render" scene !')
render = context.scene return {"CANCELLED"}
else:
render = context.scene
nodes = render.node_tree.nodes nodes = render.node_tree.nodes
if self.clear_unused_view_layers: if self.clear_unused_view_layers:

View File

@ -114,73 +114,6 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
return ng, out 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): class GPEXP_OT_merge_viewlayers_to_active(bpy.types.Operator):
bl_idname = "gp.merge_viewlayers_to_active" bl_idname = "gp.merge_viewlayers_to_active"
bl_label = "Merge selected layers view_layers" bl_label = "Merge selected layers view_layers"
@ -207,11 +140,47 @@ class GPEXP_OT_merge_viewlayers_to_active(bpy.types.Operator):
# self.report({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned') # self.report({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
# return {'CANCELLED'} # return {'CANCELLED'}
ret = merge_gplayer_viewlayers(ob, act=act, layers=layers) ret = fn.merge_gplayer_viewlayers(ob, act=act, layers=layers)
if isinstance(ret, tuple): if isinstance(ret, tuple):
self.report(*ret) self.report(*ret)
return {"FINISHED"} return {"FINISHED"}
class GPEXP_OT_auto_merge_adjacent_prefix(bpy.types.Operator):
bl_idname = "gpexp.auto_merge_adjacent_prefix"
bl_label = "Auto Merge Adjacent Prefix"
bl_description = "Automatically merge viewlayer and renderlayer of grouped layer prefix"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
excluded_prefix : bpy.props.StringProperty(
name='Excluded Prefix', default='GP,RG,PO',
description='Exclude comma separated prefix from merging viewlayer')
first_name : bpy.props.BoolProperty(name='Merge On Bottom Layer',
default=True,
description='Keep the viewlayer of the bottom layer in groups, else upper layer')
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
return self.execute(context)
def draw(self, context):
layout = self.layout
layout.label(text='Settings for auto-merge:')
layout.prop(self, 'excluded_prefix')
layout.prop(self, 'first_name')
def execute(self, context):
prefix_list = [p.strip() for p in self.excluded_prefix.split(',')]
for ob in [o for o in context.selected_objects if o.type == 'GPENCIL']:
fn.group_adjacent_layer_prefix_rlayer(ob, excluded_prefix=prefix_list, first_name=self.first_name)
return {"FINISHED"}
# unused
class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator): class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):
bl_idname = "gp.merge_selected_dopesheet_layers" bl_idname = "gp.merge_selected_dopesheet_layers"
bl_label = "Merge selected layers nodes" bl_label = "Merge selected layers nodes"
@ -313,6 +282,7 @@ class GPEXP_OT_merge_selected_viewlayer_nodes(bpy.types.Operator):
classes=( classes=(
GPEXP_OT_merge_viewlayers_to_active, GPEXP_OT_merge_viewlayers_to_active,
GPEXP_OT_auto_merge_adjacent_prefix,
GPEXP_OT_merge_selected_dopesheet_layers,# unused GPEXP_OT_merge_selected_dopesheet_layers,# unused
GPEXP_OT_merge_selected_viewlayer_nodes, GPEXP_OT_merge_selected_viewlayer_nodes,
) )

97
fn.py
View File

@ -349,6 +349,103 @@ def get_frames_bbox(node_tree):
## -- nodes helper functions ## -- nodes helper functions
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)
def group_adjacent_layer_prefix_rlayer(ob, excluded_prefix=[], first_name=True):
'''Set viewlayer and renderlayers by Gp layer adjacent prefix
Call merge_gplayer_viewlayers with grouped prefix
:excluded_prefix: List of prefixes to exclude from merge or str with comma separated values
:first_name: Keep the viewlayer of the bottom layer in group, else last
'''
from itertools import groupby
re_prefix = re.compile(r'^([A-Z]{2})_')
if isinstance(excluded_prefix, str):
excluded_prefix = [p.strip() for p in excluded_prefix.split(',')]
## Create adjacent grp list: [('CO', [layer1, layer2]), ('LN', [layer3, layer4])]
adjacent_prefix_groups = [
(g[0], list(g[1])) for g in
groupby([l for l in ob.data.layers],
key=lambda l: re_prefix.search(l.info).group(1) if re_prefix.search(l.info) else '')
]
for prefix, layer_grp in adjacent_prefix_groups:
if len(layer_grp) < 2:
continue
if not prefix or prefix in excluded_prefix:
continue
ref = layer_grp[0] if first_name else layer_grp[-1]
merge_gplayer_viewlayers(ob, act=ref, layers=layer_grp)
def clear_nodegroup(name, full_clear=False): def clear_nodegroup(name, full_clear=False):
'''remove duplication of a nodegroup (.???) '''remove duplication of a nodegroup (.???)
also remove the base one if full_clear True also remove the base one if full_clear True

View File

@ -222,6 +222,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
else: else:
scene = fn.get_render_scene() scene = fn.get_render_scene()
print('Set viewlayer Scene: ', scene.name)
node_tree = scene.node_tree node_tree = scene.node_tree
nodes = node_tree.nodes nodes = node_tree.nodes
@ -371,3 +372,20 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
fn.rearrange_frames(node_tree) fn.rearrange_frames(node_tree)
return vl, cp return vl, cp
def export_gp_objects(oblist, exclude_list=[], scene=None):
# Skip layer containing element in exclude list
if not isinstance(oblist, list):
oblist = [oblist]
if isinstance(exclude_list, str):
exclude_list = [p.strip() for p in exclude_list.split(',')]
for ob in oblist:
for l in ob.data.layers:
# if l.hide:
# continue
if l.hide or any(x + '_' in l.info for x in exclude_list): # exclude hided ?
l.viewlayer_render = fn.get_view_layer('exclude', scene=scene).name # assign "exclude"
continue
_vl, _cp = get_set_viewlayer_from_gp(ob, l, scene=scene) # scene=fn.get_render_scene())

2
ui.py
View File

@ -204,6 +204,8 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
# merge layers from dopesheet # merge layers from dopesheet
row.operator('gp.merge_viewlayers_to_active', text=txt, icon='SELECT_EXTEND') row.operator('gp.merge_viewlayers_to_active', text=txt, icon='SELECT_EXTEND')
row.enabled= ct > 1 row.enabled= ct > 1
col.operator('gpexp.auto_merge_adjacent_prefix', icon='SELECT_EXTEND')
## all and objects ## all and objects
layout.separator() layout.separator()