trailing whitespaces cleanup
parent
f3646e37df
commit
12cce98e41
|
@ -17,7 +17,7 @@ Activate / deactivate all masks using MA layers
|
|||
1.2.1
|
||||
|
||||
- added: bundle app_template to load "GR Render" workspace from it
|
||||
- added: ui button in dopesheet to load GP render workspace if not loaded
|
||||
- added: ui button in dopesheet to load GP render workspace
|
||||
|
||||
1.2.0
|
||||
|
||||
|
@ -91,7 +91,7 @@ Activate / deactivate all masks using MA layers
|
|||
|
||||
0.9.4
|
||||
|
||||
- feat: `Renumber files on disk` option using number in file outputs (under advanced gp render options)
|
||||
- feat: `Renumber files on disk` option using number in file outputs (under advanced gp render options)
|
||||
- feat: new `Check for problems` button, check if problem in layer state, missing file out, broken gp modifier target and report
|
||||
- added: clean nodes now also rearrange inside nodegroup
|
||||
- changed: `Check layers` now trigger `export layer infos` automatically.
|
||||
|
@ -121,7 +121,7 @@ Activate / deactivate all masks using MA layers
|
|||
- feat: Select a file output node. Set active file slot path and settings to main Scene output.
|
||||
- Button in GP render panel with `Advanced` options active.
|
||||
- Or search operator label `Set Active File Output To Composite`
|
||||
- if Composite is already linked, pop-up ask if link should be replaced
|
||||
- if Composite is already linked, pop-up ask if link should be replaced
|
||||
|
||||
0.7.0
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
|||
if not layer:
|
||||
self.report({'ERROR'}, 'No active layer')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
ct = 0
|
||||
# send scene ?
|
||||
hided = 0
|
||||
|
@ -30,7 +30,7 @@ class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
|||
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
|
||||
|
@ -64,7 +64,7 @@ class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
|
|||
else:
|
||||
scn = context.scene
|
||||
|
||||
|
||||
|
||||
excludes = [] # ['MA', 'IN'] # Get list dynamically
|
||||
if self.mode == 'SELECTED':
|
||||
gen_vlayer.export_gp_objects([o for o in context.selected_objects if o.type == 'GPENCIL'], exclude_list=excludes, scene=scn)
|
||||
|
@ -106,7 +106,7 @@ GPEXP_OT_add_objects_to_render,
|
|||
GPEXP_OT_split_to_scene,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ def batch_setup_render_scene(context=None, render_scn=None):
|
|||
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')
|
||||
|
@ -66,42 +66,42 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'GPENCIL'
|
||||
|
||||
|
||||
# timer : bpy.props.FloatProperty(default=0.1, options={'SKIP_SAVE'})
|
||||
|
||||
excluded_prefix : bpy.props.StringProperty(
|
||||
name='Excluded Layer By Prefix', default='GP, RG, PO, MA',
|
||||
description='Exclude layer to send to render by prefix (comma separated list)')
|
||||
|
||||
clean_name_and_visibility : bpy.props.BoolProperty(name='Clean Name And Visibility', default=True,
|
||||
clean_name_and_visibility : bpy.props.BoolProperty(name='Clean Name And Visibility', default=True,
|
||||
description='Add object name to layer name when there is only prefix (ex: "CO_")\
|
||||
\nEnable visibility for layer with prefix included in Prefix Filter')
|
||||
|
||||
clean_material_duplication : bpy.props.BoolProperty(name='Clean Material Duplication', default=True,
|
||||
|
||||
clean_material_duplication : bpy.props.BoolProperty(name='Clean Material Duplication', default=True,
|
||||
description='Clean material stack. i.e: Replace "mat.001" in material stack if "mat" exists and has same color')
|
||||
|
||||
prefix_filter : bpy.props.StringProperty(name='Prefix Filter', default='CO, CU, FX, TO', # , MA # exclude MA if mask are applied
|
||||
description='Comma separated prefix to render. Set the other prefix and non-prefixed layer to exluded viewlayer')
|
||||
|
||||
set_layers_colors : bpy.props.BoolProperty(name='Set Layers Colors', default=True,
|
||||
set_layers_colors : bpy.props.BoolProperty(name='Set Layers Colors', default=True,
|
||||
description='')
|
||||
|
||||
trigger_rename_lowercase : bpy.props.BoolProperty(name='Trigger Rename Lowercase', default=True,
|
||||
trigger_rename_lowercase : bpy.props.BoolProperty(name='Trigger Rename Lowercase', default=True,
|
||||
description='')
|
||||
|
||||
trigger_renumber_by_distance : bpy.props.BoolProperty(name='Trigger Renumber By Distance', default=True,
|
||||
trigger_renumber_by_distance : bpy.props.BoolProperty(name='Trigger Renumber By Distance', default=True,
|
||||
description='')
|
||||
|
||||
export_layer_infos : bpy.props.BoolProperty(name='Export Layer Infos', default=True,
|
||||
export_layer_infos : bpy.props.BoolProperty(name='Export Layer Infos', default=True,
|
||||
description='')
|
||||
|
||||
group_all_adjacent_layer_type : bpy.props.BoolProperty(name='Group All Adjacent Layer Type', default=True,
|
||||
group_all_adjacent_layer_type : bpy.props.BoolProperty(name='Group All Adjacent Layer Type', default=True,
|
||||
description='')
|
||||
|
||||
change_to_gp_workspace : bpy.props.BoolProperty(name='Change To Gp Workspace', default=True,
|
||||
change_to_gp_workspace : bpy.props.BoolProperty(name='Change To Gp Workspace', default=True,
|
||||
description='')
|
||||
|
||||
batch_setup_render_scene : bpy.props.BoolProperty(name='Batch Setup Render Scene', default=True,
|
||||
batch_setup_render_scene : bpy.props.BoolProperty(name='Batch Setup Render Scene', default=True,
|
||||
description='')
|
||||
|
||||
|
||||
|
@ -116,7 +116,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
row = col.row()
|
||||
row.prop(self, 'prefix_filter')
|
||||
row.active = self.clean_name_and_visibility
|
||||
|
||||
|
||||
col.prop(self, 'clean_material_duplication')
|
||||
|
||||
col.prop(self, 'set_layers_colors')
|
||||
|
@ -132,7 +132,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
col.prop(self, 'batch_setup_render_scene')
|
||||
|
||||
# layout.prop(self, 'clear_unused_view_layers')
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
print('-- Auto-build Render scene --\n')
|
||||
|
||||
|
@ -146,9 +146,9 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
if render_scn:
|
||||
self.report({'ERROR'}, 'A "Render" scene already exists')
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
all_gp_objects = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
||||
|
||||
|
||||
## clean name and visibility
|
||||
if self.clean_name_and_visibility:
|
||||
for o in all_gp_objects:
|
||||
|
@ -159,7 +159,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
## Clean name when layer has no name after prefix
|
||||
if re.match(r'^[A-Z]{2}_$', l.info):
|
||||
l.info = l.info + o.name.lower()
|
||||
|
||||
|
||||
## Make used prefix visible ?? (maybe some layer were intentionally hidden...)
|
||||
if (res := re.search(r'^([A-Z]{2})_', l.info)):
|
||||
if res.group(1) in prefix_to_render and l.hide == True and not 'invisible' in l.info:
|
||||
|
@ -202,7 +202,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
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)
|
||||
if self.export_layer_infos:
|
||||
print('Export layer infos (skip if json already exists)')
|
||||
|
@ -221,7 +221,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
return {'CANCELLED'}
|
||||
|
||||
context.window.scene = render_scn
|
||||
|
||||
|
||||
## Group all adjacent layer type
|
||||
if self.group_all_adjacent_layer_type:
|
||||
print('Group all adjacent layer type')
|
||||
|
@ -252,7 +252,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
|||
|
||||
## Batch setup render scene
|
||||
batch_setup_render_scene(render_scn=render_scn)
|
||||
|
||||
|
||||
## No need for timer anymore !
|
||||
# if batch_setup_render_scene:
|
||||
# if self.timer > 0:
|
||||
|
@ -296,7 +296,7 @@ GPEXP_OT_render_auto_build,
|
|||
GPEXP_OT_render_scene_setup,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ def check_layer_state(pool=None, reports=None):
|
|||
# # all masks disable
|
||||
# pass
|
||||
|
||||
## just list masks
|
||||
# state = '' if l.use_mask_layer else ' (disabled)'
|
||||
## just list masks
|
||||
# state = '' if l.use_mask_layer else ' (disabled)'
|
||||
# reports.append(f'{ob.name} > {l.info} masks{state}:')
|
||||
# for ml in l.mask_layers:
|
||||
# mlstate = ' (disabled)' if ml.hide else ''
|
||||
|
@ -60,7 +60,7 @@ def check_file_output_numbering(reports=None):
|
|||
if S.name == 'Scene' or not S.node_tree or not S.use_nodes:
|
||||
continue
|
||||
file_outs += [n for n in S.node_tree.nodes if n.type == 'OUTPUT_FILE']
|
||||
|
||||
|
||||
used=False
|
||||
|
||||
if not file_outs:
|
||||
|
@ -84,7 +84,7 @@ class GPEXP_OT_check_render_scene(bpy.types.Operator):
|
|||
bl_description = "Auto check render scene"
|
||||
bl_options = {"REGISTER"} # , "UNDO"
|
||||
|
||||
# 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",
|
||||
# default=True)
|
||||
|
||||
|
@ -100,7 +100,7 @@ class GPEXP_OT_check_render_scene(bpy.types.Operator):
|
|||
layout = self.layout
|
||||
# layout.prop(self, 'clear_unused_view_layers')
|
||||
|
||||
def execute(self, context):
|
||||
def execute(self, context):
|
||||
reports = []
|
||||
# check gp modifiers
|
||||
broken_mods = check_broken_modifier_target()
|
||||
|
@ -108,7 +108,7 @@ class GPEXP_OT_check_render_scene(bpy.types.Operator):
|
|||
reports.append('GP modifiers targets:')
|
||||
reports += broken_mods
|
||||
|
||||
# check layers
|
||||
# check layers
|
||||
layer_state = check_layer_state()
|
||||
if layer_state:
|
||||
if reports: reports.append('')
|
||||
|
@ -137,7 +137,7 @@ classes=(
|
|||
GPEXP_OT_check_render_scene,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
32
OP_clean.py
32
OP_clean.py
|
@ -15,12 +15,12 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
|||
|
||||
# mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
def execute(self, context):
|
||||
render = bpy.data.scenes.get('Render')
|
||||
if not render:
|
||||
print('SKIP, no Render scene')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
print('re-arranging frames')
|
||||
fn.rearrange_frames(render.node_tree)
|
||||
|
||||
|
@ -39,7 +39,7 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
|||
break
|
||||
if out:
|
||||
fn.reorder_fileout(out, ng=n)
|
||||
|
||||
|
||||
|
||||
## clear disconnected fileout ??...
|
||||
# for fo in render.node_tree.nodes:
|
||||
|
@ -57,31 +57,31 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
|||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
# Internal prop (use when launching from python)
|
||||
use_render_scene : bpy.props.BoolProperty(name="Use Render Scene",
|
||||
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",
|
||||
default=True)
|
||||
|
||||
arrange_rl_nodes : bpy.props.BoolProperty(name="Arrange Render Node In Frames",
|
||||
arrange_rl_nodes : bpy.props.BoolProperty(name="Arrange Render Node In Frames",
|
||||
description="Re-arrange Render Layer Nodes Y positions within each existing frames" ,
|
||||
default=True)
|
||||
|
||||
arrange_frames : bpy.props.BoolProperty(name="Arrange Frames",
|
||||
arrange_frames : bpy.props.BoolProperty(name="Arrange Frames",
|
||||
description="Re-arrange all frames Y positions" ,
|
||||
default=True)
|
||||
|
||||
reorder_inputs : bpy.props.BoolProperty(name="Reorder I/O Sockets",
|
||||
reorder_inputs : bpy.props.BoolProperty(name="Reorder I/O Sockets",
|
||||
description="Reorder inputs/outputs of all 'NG_' nodegroup and their connected file output",
|
||||
default=True)
|
||||
|
||||
clear_isolated_node_in_groups : bpy.props.BoolProperty(name="Clear Isolated Node In Groups",
|
||||
clear_isolated_node_in_groups : bpy.props.BoolProperty(name="Clear Isolated Node In Groups",
|
||||
description="Clean content of 'NG_' nodegroup bpy deleting isolated nodes)",
|
||||
default=True)
|
||||
|
||||
fo_clear_disconnected : bpy.props.BoolProperty(name="Remove Disconnected Export Inputs",
|
||||
fo_clear_disconnected : bpy.props.BoolProperty(name="Remove Disconnected Export Inputs",
|
||||
description="Clear any disconnected intput of every 'file output' node",
|
||||
default=False)
|
||||
|
||||
|
@ -104,7 +104,7 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
|||
layout.prop(self, 'fo_clear_disconnected')
|
||||
if self.fo_clear_disconnected:
|
||||
layout.label(text='Disconnected inputs are not exported', icon='INFO')
|
||||
|
||||
|
||||
# box = layout.box()
|
||||
# box.prop(self, 'arrange_frames')
|
||||
# box.prop(self, 'reorder_inputs')
|
||||
|
@ -152,17 +152,17 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
|||
break
|
||||
if out:
|
||||
fn.reorder_fileout(out, ng=n)
|
||||
|
||||
|
||||
# Clear input that do not exists
|
||||
fn.clean_nodegroup_inputs(n, skip_existing_pass=True)
|
||||
|
||||
|
||||
fn.bridge_reconnect_nodegroup(n)
|
||||
|
||||
|
||||
if self.clear_isolated_node_in_groups:
|
||||
for n in nodes:
|
||||
if n.type != 'GROUP' or not n.name.startswith('NG_'):
|
||||
continue
|
||||
fn.clear_nodegroup_content_if_disconnected(n.node_tree)
|
||||
fn.clear_nodegroup_content_if_disconnected(n.node_tree)
|
||||
|
||||
if self.fo_clear_disconnected:
|
||||
for fo in nodes:
|
||||
|
@ -179,7 +179,7 @@ classes=(
|
|||
GPEXP_OT_clean_compo_tree,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
10
OP_clear.py
10
OP_clear.py
|
@ -14,20 +14,20 @@ class GPEXP_OT_clear_render_tree(bpy.types.Operator):
|
|||
mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
|
||||
render = bpy.data.scenes.get('Render')
|
||||
if not render:
|
||||
print('SKIP, no Render scene')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
# clear all nodes in frames
|
||||
if render.use_nodes:
|
||||
for i in range(len(render.node_tree.nodes))[::-1]:
|
||||
|
||||
|
||||
# skip frames to delete later
|
||||
if render.node_tree.nodes[i].type == 'FRAME':
|
||||
continue
|
||||
|
||||
|
||||
# skip unparented nodes
|
||||
if not render.node_tree.nodes[i].parent:
|
||||
continue
|
||||
|
@ -59,7 +59,7 @@ classes=(
|
|||
GPEXP_OT_clear_render_tree,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
|
|||
|
||||
# mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
def execute(self, context):
|
||||
node_tree = context.scene.node_tree
|
||||
nodes = node_tree.nodes
|
||||
|
||||
|
||||
changed = []
|
||||
for n in nodes:
|
||||
if not n.select or not n.type == 'R_LAYERS':
|
||||
|
@ -25,10 +25,10 @@ class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
|
|||
|
||||
if not ' / ' in n.layer:
|
||||
continue
|
||||
|
||||
|
||||
if n.outputs[0].is_linked: # already connected
|
||||
continue
|
||||
|
||||
|
||||
# get namme
|
||||
obname = n.layer.split()[0]
|
||||
grp_name = f'NG_{obname}'
|
||||
|
@ -39,7 +39,7 @@ class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
|
|||
if not grp:
|
||||
print(f'{n.name} Node group not found : {n.layer} !-> {grp_name}')
|
||||
continue
|
||||
|
||||
|
||||
inp = grp.inputs.get(n.layer)
|
||||
if not inp:
|
||||
print(f'{n.name} no inputs name "{n.layer}" in group {grp_name}')
|
||||
|
@ -48,7 +48,7 @@ class GPEXP_OT_reconnect_render_layer(bpy.types.Operator):
|
|||
# reconnect
|
||||
node_tree.links.new(n.outputs[0], inp)
|
||||
changed.append(f'{n.name} ({n.layer}) to {grp_name}')
|
||||
|
||||
|
||||
if changed:
|
||||
self.report({'INFO'}, f'{len(changed)} nodes reconnected')
|
||||
else:
|
||||
|
@ -68,8 +68,8 @@ class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
|||
|
||||
# mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
rd_scn = bpy.data.scenes.get('Render')
|
||||
if not rd_scn:
|
||||
self.report({'ERROR'}, 'Viewlayers needs to be generated first!')
|
||||
|
@ -81,7 +81,7 @@ class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
|||
# 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]
|
||||
|
@ -115,7 +115,7 @@ class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
|||
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)
|
||||
|
@ -125,7 +125,7 @@ class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
|||
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)
|
||||
|
||||
|
@ -160,7 +160,7 @@ class GPEXP_OT_set_active_fileout_to_compout(bpy.types.Operator):
|
|||
if not len(self.fo.file_slots):
|
||||
self.report({'ERROR'}, 'no slots in active file output')
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
# check if active slot has a source
|
||||
if not self.fo.inputs[self.fo.active_input_index].is_linked:
|
||||
return self.execute(context)
|
||||
|
@ -170,11 +170,11 @@ class GPEXP_OT_set_active_fileout_to_compout(bpy.types.Operator):
|
|||
if not out or not out.inputs[0].is_linked:
|
||||
self.compo_out_from_link = ''
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
# compo linked, pop panel to choose replace or not
|
||||
self.compo_out_from_link = out.inputs[0].links[0].from_node.name
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
@ -189,11 +189,11 @@ class GPEXP_OT_set_active_fileout_to_compout(bpy.types.Operator):
|
|||
idx = self.fo.active_input_index
|
||||
sl = self.fo.file_slots[idx]
|
||||
sk = self.fo.inputs[idx]
|
||||
|
||||
|
||||
if not sk.is_linked:
|
||||
self.report({'INFO'}, f'Outut changed to match {sl.path} (slot was not linked)')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
## If linked replace links to Composite node
|
||||
if not self.relink_composite:
|
||||
return {'FINISHED'}
|
||||
|
@ -201,16 +201,16 @@ class GPEXP_OT_set_active_fileout_to_compout(bpy.types.Operator):
|
|||
ntree = context.scene.node_tree
|
||||
links = context.scene.node_tree.links
|
||||
nodes = context.scene.node_tree.nodes
|
||||
|
||||
|
||||
out = nodes.get('Composite')
|
||||
if not out:
|
||||
out = fn.create_node('COMPOSITE', tree=ntree)
|
||||
fo_loc = fn.real_loc(self.fo)
|
||||
out.location = (fo_loc.x, fo_loc.y + 160)
|
||||
|
||||
|
||||
# if out.inputs[0].is_linked:
|
||||
# self.report({'WARNING'}, f'Outut changed to match {sl.path} (Composite node already linked)')
|
||||
|
||||
|
||||
lnk = sk.links[0]
|
||||
from_sk = sk.links[0].from_socket
|
||||
links.remove(lnk)
|
||||
|
@ -226,7 +226,7 @@ GPEXP_OT_delete_render_layer,
|
|||
GPEXP_OT_set_active_fileout_to_compout,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ GPEXP_OT_set_crop_from_selection,
|
|||
GPEXP_OT_export_crop_coord_to_json,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ def Export_AE_2d_position_json_data():
|
|||
if not cam:
|
||||
print('Active camera not "anim_cam"')
|
||||
cam = scn.camera
|
||||
|
||||
|
||||
rx = scn.render
|
||||
rx, ry = rd.resolution_x, rd.resolution_y
|
||||
|
||||
|
@ -49,7 +49,7 @@ def export_AE_objects_position_keys():
|
|||
result = {}
|
||||
print(f'Exporting 2d position (scene range: {scn.frame_start} - {scn.frame_end})')
|
||||
for fr in range(scn.frame_start,scn.frame_end + 1):
|
||||
|
||||
|
||||
print(f'frame: {fr}')
|
||||
scn.frame_set(fr)
|
||||
|
||||
|
@ -57,17 +57,17 @@ def export_AE_objects_position_keys():
|
|||
if not result.get(o.name):
|
||||
result[o.name] = []
|
||||
proj2d = world_to_camera_view(scn, scn.camera, o.matrix_world.to_translation()) # + Vector((.5,.5,0))
|
||||
|
||||
|
||||
# proj2d = correct_shift(proj2d, scn.camera) # needed ?
|
||||
x = (proj2d[0]) * scn.render.resolution_x
|
||||
y = -(proj2d[1]) * scn.render.resolution_y + scn.render.resolution_y
|
||||
|
||||
|
||||
result[o.name].append((fr,x,y))
|
||||
|
||||
for name,value in result.items():
|
||||
|
||||
txt = fn.get_ae_keyframe_clipboard_header(scn)
|
||||
|
||||
|
||||
for v in value:
|
||||
txt += '\t%s\t%s\t%s\t0\t\n'%(v[0],v[1],v[2]) # add 0 for Z (probably not needed)
|
||||
|
||||
|
@ -76,12 +76,12 @@ def export_AE_objects_position_keys():
|
|||
blend = Path(bpy.data.filepath)
|
||||
keyfile = blend.parent / 'render' / f'pos_{name}.txt'
|
||||
keyfile.parent.mkdir(parents=False, exist_ok=True)
|
||||
|
||||
|
||||
print(f'exporting keys for {name} at {keyfile}')
|
||||
|
||||
|
||||
## save forcing CRLF terminator (DOS style, damn windows)
|
||||
## in case it's exported from linux
|
||||
with open(keyfile, 'w', newline='\r\n') as fd:
|
||||
with open(keyfile, 'w', newline='\r\n') as fd:
|
||||
fd.write(txt)
|
||||
|
||||
|
||||
|
@ -178,7 +178,7 @@ class GPEXP_OT_fix_overscan_shift(bpy.types.Operator):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
|
||||
if self.use_selection:
|
||||
col = layout.column()
|
||||
col.label(text=f'Camera "{self.cam_ob.name}" selected', icon='INFO')
|
||||
|
@ -196,7 +196,7 @@ class GPEXP_OT_fix_overscan_shift(bpy.types.Operator):
|
|||
|
||||
def execute(self, context):
|
||||
cam = self.cam_ob.data
|
||||
|
||||
|
||||
ratio_x = self.init_rx / context.scene.render.resolution_x
|
||||
ratio_y = self.init_ry / context.scene.render.resolution_y
|
||||
|
||||
|
@ -212,7 +212,7 @@ class GPEXP_OT_fix_overscan_shift(bpy.types.Operator):
|
|||
else:
|
||||
if cam.shift_x != 1:
|
||||
cam.shift_x = cam.shift_x * ratio_x
|
||||
|
||||
|
||||
if ratio_y != 1:
|
||||
if fn.has_keyframe(cam, 'shift_y'):
|
||||
fcu = cam.animation_data.action.fcurves.find('shift_y')
|
||||
|
@ -260,7 +260,7 @@ GPEXP_OT_fix_overscan_shift,
|
|||
GPEXP_PT_extra_gprender_func
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
@ -274,4 +274,3 @@ def unregister():
|
|||
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
|
@ -34,7 +34,7 @@ class GPEXP_OT_number_outputs(bpy.types.Operator):
|
|||
return True
|
||||
|
||||
mode : bpy.props.StringProperty(default='SELECTED', options={'SKIP_SAVE'})
|
||||
# ctrl : bpy.props.StringProperty(default=False, options={'SKIP_SAVE'}) # no need
|
||||
# ctrl : bpy.props.StringProperty(default=False, options={'SKIP_SAVE'}) # no need
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.ctrl = event.ctrl
|
||||
|
@ -45,7 +45,7 @@ class GPEXP_OT_number_outputs(bpy.types.Operator):
|
|||
if not render:
|
||||
print('SKIP, no Render scene')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
ct = 0
|
||||
nodes = render.node_tree.nodes
|
||||
for fo in nodes:
|
||||
|
@ -95,7 +95,7 @@ class GPEXP_OT_set_output_node_format(bpy.types.Operator):
|
|||
for n in nodes:
|
||||
if n.type != 'OUTPUT_FILE' or n == ref or not n.select:
|
||||
continue
|
||||
|
||||
|
||||
for attr in dir(ref.format):
|
||||
if attr.startswith('__') or attr in {'rna_type','bl_rna', 'view_settings', 'display_settings','stereo_3d_format'}: # views_format
|
||||
continue
|
||||
|
@ -104,10 +104,10 @@ class GPEXP_OT_set_output_node_format(bpy.types.Operator):
|
|||
except Exception as e:
|
||||
print(f"can't set attribute : {attr}")
|
||||
|
||||
# n.format.file_format = file_format
|
||||
# n.format.file_format = file_format
|
||||
# n.format.color_mode = color_mode
|
||||
# n.format.color_depth = color_depth
|
||||
# n.format.compression = compression
|
||||
# n.format.color_depth = color_depth
|
||||
# n.format.compression = compression
|
||||
|
||||
ct += 1
|
||||
|
||||
|
@ -157,7 +157,7 @@ class GPEXP_OT_normalize_outnames(bpy.types.Operator):
|
|||
fp_l = reslash.split(fp)
|
||||
for i, part in enumerate(fp_l):
|
||||
fp_l[1] = re.sub(r'(^\d{3}_)?([A-Z]{2}_)?(.*?)(_[A-Z]{2})?(_)?', out_norm, part)
|
||||
|
||||
|
||||
fs.path = '/'.join(fp_l)
|
||||
|
||||
ct += 1
|
||||
|
@ -183,7 +183,7 @@ class GPEXP_OT_enable_all_viewlayers(bpy.types.Operator):
|
|||
vl_list = [vl for vl in 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"}
|
||||
|
||||
|
@ -206,7 +206,7 @@ class GPEXP_OT_activate_only_selected_layers(bpy.types.Operator):
|
|||
vls = [scn.view_layers.get(n.layer) for n in rlayers_nodes if scn.view_layers.get(n.layer)]
|
||||
for v in scn.view_layers:
|
||||
v.use = v in vls
|
||||
|
||||
|
||||
self.report({"INFO"}, f'Now only {len(vls)} viewlayer active (/{len(scn.view_layers)})')
|
||||
return {"FINISHED"}
|
||||
|
||||
|
@ -224,25 +224,25 @@ class GPEXP_OT_reset_render_settings(bpy.types.Operator):
|
|||
if scn.name == 'Scene':
|
||||
# don't touch original scene
|
||||
continue
|
||||
|
||||
|
||||
# set a unique preview output
|
||||
# - avoid possible write/sync overlap (point to tmp on linux ?)
|
||||
# - allow to monitor output of a scene and possibly use Overwrite
|
||||
|
||||
|
||||
if scn.render.filepath.startswith('//render/preview/'):
|
||||
scn.render.filepath = f'//render/preview/{bpy.path.clean_name(scn.name.lower())}/preview_'
|
||||
print(f'Scene {scn.name}: change output to {scn.render.filepath}')
|
||||
|
||||
|
||||
if not scn.use_nodes:
|
||||
continue
|
||||
|
||||
|
||||
# set the settings depending on merges node presences
|
||||
use_native_aa = True
|
||||
for n in scn.node_tree.nodes:
|
||||
if n.name.startswith('merge_NG_'):
|
||||
use_native_aa = False
|
||||
break
|
||||
|
||||
|
||||
if scn.use_aa != use_native_aa:
|
||||
print(f'Scene {scn.name}: changed scene AA settings, native AA = {use_native_aa}')
|
||||
fn.scene_aa(scene=scn, toggle=use_native_aa)
|
||||
|
@ -262,7 +262,7 @@ GPEXP_OT_reset_render_settings,
|
|||
# GPEXP_OT_normalize_outnames,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
|
|||
# get node group
|
||||
# ng = rlayers[0].outputs[0].links[0].to_node
|
||||
|
||||
# sort RL descending
|
||||
# sort RL descending
|
||||
rlayers.sort(key=lambda n: fn.real_loc(n).y, reverse=True)
|
||||
|
||||
node_tree = rlayers[0].id_data
|
||||
|
@ -41,11 +41,11 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
|
|||
obname = lname = bpy.path.clean_name(vl_name)
|
||||
base_path = f'//render/'
|
||||
slot_name = f'{lname}/{lname}_'
|
||||
|
||||
|
||||
|
||||
# change colors of those nodes
|
||||
disconnected_groups = []
|
||||
if not color:
|
||||
if not color:
|
||||
color = fn.random_color()
|
||||
for n in rlayers:
|
||||
n.use_custom_color = True
|
||||
|
@ -57,7 +57,7 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
|
|||
if lnk.to_node.name.startswith('NG_'):
|
||||
disconnected_groups.append(lnk.to_node)
|
||||
links.remove(lnk)
|
||||
|
||||
|
||||
disconnected_groups = list(set(disconnected_groups))
|
||||
|
||||
ng_name = f'merge_NG_{obname}' # only object name
|
||||
|
@ -72,7 +72,7 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
|
|||
while bpy.data.node_groups.get(ng_name): # nodes.get(ng_name)
|
||||
if not re.search(r'(\d+)$', ng_name):
|
||||
ng_name += '_02' # if not ending with a number add _02
|
||||
ng_name = re.sub(r'(\d+)(?!.*\d)', lambda x: str(int(x.group(1))+1).zfill(len(x.group(1))), ng_name)
|
||||
ng_name = re.sub(r'(\d+)(?!.*\d)', lambda x: str(int(x.group(1))+1).zfill(len(x.group(1))), ng_name)
|
||||
|
||||
# print(f'create merge nodegroup {ng_name}')
|
||||
ngroup = bpy.data.node_groups.new(ng_name, 'CompositorNodeTree')
|
||||
|
@ -101,7 +101,7 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
|
|||
out.base_path = base_path
|
||||
out.file_slots.new(slot_name)
|
||||
links.new(ng.outputs[0], out.inputs[-1])
|
||||
|
||||
|
||||
fn.clear_disconnected(out)
|
||||
out.update()
|
||||
|
||||
|
@ -139,7 +139,7 @@ class GPEXP_OT_merge_viewlayers_to_active(bpy.types.Operator):
|
|||
# if not act.viewlayer_render:
|
||||
# self.report({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
|
||||
# return {'CANCELLED'}
|
||||
|
||||
|
||||
ret = fn.merge_gplayer_viewlayers(ob, act=act, layers=layers)
|
||||
if isinstance(ret, tuple):
|
||||
self.report(*ret)
|
||||
|
@ -159,9 +159,9 @@ class GPEXP_OT_auto_merge_adjacent_prefix(bpy.types.Operator):
|
|||
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,
|
||||
|
||||
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):
|
||||
|
@ -209,7 +209,7 @@ class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):
|
|||
render = bpy.data.scenes.get('Render')
|
||||
if render:
|
||||
nodes = render.node_tree.nodes
|
||||
|
||||
|
||||
clean_ob_name = bpy.path.clean_name(ob.name)
|
||||
rlayers = []
|
||||
for l in layers:
|
||||
|
@ -226,16 +226,16 @@ class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):
|
|||
if not rlayer:
|
||||
# send to function to generate the rlayer and connect
|
||||
_vl, rl = gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||
|
||||
|
||||
else:
|
||||
rlayer.sort(key=lambda n: n.location.y, reverse=True)
|
||||
rl = rlayer[0]
|
||||
|
||||
|
||||
if act == l:
|
||||
nodes.active = rl # make it active so the merge use this one
|
||||
|
||||
rlayers.append(rl)
|
||||
|
||||
|
||||
color = None
|
||||
if fn.has_channel_color(act): # and bpy.context.preferences.edit.use_anim_channel_group_colors
|
||||
color = act.channel_color
|
||||
|
@ -287,7 +287,7 @@ GPEXP_OT_merge_selected_dopesheet_layers,# unused
|
|||
GPEXP_OT_merge_selected_viewlayer_nodes,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -42,15 +42,15 @@ def renumber_sequence_on_disk_from_file_slots(apply=True, active_scene_only=Fals
|
|||
obj_num = prenum.search(obj_full)
|
||||
if obj_num:
|
||||
obj_num = obj_num.group(0)
|
||||
|
||||
|
||||
## check if folder exists
|
||||
folder_path = None
|
||||
|
||||
|
||||
for d in os.scandir(render):
|
||||
if d.is_dir() and prenum.sub('', d.name) == obj:
|
||||
folder_path = render / d.name
|
||||
break
|
||||
|
||||
|
||||
if not folder_path:
|
||||
print(f'Could not find obj folder for: {obj}')
|
||||
continue
|
||||
|
@ -67,20 +67,20 @@ def renumber_sequence_on_disk_from_file_slots(apply=True, active_scene_only=Fals
|
|||
continue # If no img_num no point in renaming sequences
|
||||
|
||||
img_dir_path = None
|
||||
|
||||
|
||||
for img_dir in os.scandir(folder_path):
|
||||
if img_dir.is_dir() and prenum.sub('', img_dir.name) == img:
|
||||
img_dir_path = folder_path / img_dir.name
|
||||
break
|
||||
|
||||
|
||||
if not img_dir_path:
|
||||
print(f'Could not find img folder for: {img}')
|
||||
continue
|
||||
|
||||
|
||||
# if folder exists check if full name is ok
|
||||
if img_full == img_dir_path.name:
|
||||
continue # name already (maybe not in sequence but should be good)
|
||||
|
||||
|
||||
|
||||
# rename sequence and image folder
|
||||
for frame in os.scandir(img_dir_path):
|
||||
|
@ -91,7 +91,7 @@ def renumber_sequence_on_disk_from_file_slots(apply=True, active_scene_only=Fals
|
|||
if apply:
|
||||
fp = Path(frame.path)
|
||||
fp.rename(fp.parent / good)
|
||||
|
||||
|
||||
# rename image folder
|
||||
if img_dir_path.name != img_full:
|
||||
print(f' dir:{img_dir_path.name} > {img_full}')
|
||||
|
@ -120,11 +120,11 @@ class GPEXP_OT_renumber_files_on_disk(bpy.types.Operator):
|
|||
def invoke(self, context, event):
|
||||
# return self.execute(context)
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
dry_run: bpy.props.BoolProperty(name='Dry-run (no actions, prints in console only)',
|
||||
default=False,
|
||||
description='Test mode. If checked, no action is actually performed')
|
||||
|
||||
|
||||
active_scene_only: bpy.props.BoolProperty(name='Only Active Scene',
|
||||
default=False,
|
||||
description='use only file output of active scene instead of all scenes (skipping "Scene")')
|
||||
|
@ -158,7 +158,7 @@ classes=(
|
|||
GPEXP_OT_renumber_files_on_disk,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ def export_all_selected_frame_as_svg():
|
|||
if ob.type != 'GPENCIL':
|
||||
continue
|
||||
frames += [f.frame_number for l in ob.data.layers if not l.hide for f in l.frames if len(f.strokes)]
|
||||
|
||||
|
||||
if frames:
|
||||
frames = sorted(list(set(frames)))
|
||||
else:
|
||||
|
@ -59,7 +59,7 @@ def export_all_selected_frame_as_svg():
|
|||
if fp.exists():
|
||||
print(f' already exists: {fp}')
|
||||
continue
|
||||
|
||||
|
||||
bpy.context.scene.frame_current = fnum
|
||||
bpy.ops.wm.gpencil_export_svg(filepath=str(fp),
|
||||
check_existing=True,
|
||||
|
@ -103,16 +103,16 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
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'}
|
||||
|
||||
|
||||
|
||||
### store
|
||||
## dict all visible objects as key with value : sub dict {layer : hide_bool}
|
||||
|
||||
|
||||
# obj_vis = [[o, o.hide_viewport, o.hide_render] for o in context.scene.objects if o.type == 'GPENCIL' and not (o.hide_get() or o.hide_viewport)]
|
||||
t0 = time()
|
||||
|
||||
|
@ -122,7 +122,7 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
selection = [o for o in context.selected_objects]
|
||||
|
||||
messages = []
|
||||
|
||||
|
||||
## adaptative resampling on all concerned objects
|
||||
for ob in store.keys():
|
||||
mod = ob.grease_pencil_modifiers.get('resample')
|
||||
|
@ -136,7 +136,7 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
if ob.type != 'GPENCIL':
|
||||
continue
|
||||
|
||||
mess = f'--- {ob.name}:'
|
||||
mess = f'--- {ob.name}:'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
|
||||
|
@ -149,14 +149,14 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
# for o in context.scene.objects:
|
||||
# o.hide_viewport = True
|
||||
# ob.hide_viewport = False
|
||||
|
||||
|
||||
## manage layers
|
||||
gpl = ob.data.layers
|
||||
vl_dicts = {vl_name: list(layer_grp) for vl_name, layer_grp in groupby(gpl, lambda x: x.viewlayer_render)}
|
||||
for vl_name, layer_list in vl_dicts.items():
|
||||
vl = context.scene.view_layers.get(vl_name)
|
||||
if not vl:
|
||||
mess = f'/!\ {vl_name} viewlayer not exists : skipped {[l.info for l in layer_list]}'
|
||||
mess = f'/!\ {vl_name} viewlayer not exists : skipped {[l.info for l in layer_list]}'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
continue
|
||||
|
@ -164,11 +164,11 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
continue
|
||||
|
||||
if not vl.use:
|
||||
mess = f'{vl_name} viewlayer disabled'
|
||||
mess = f'{vl_name} viewlayer disabled'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
continue
|
||||
|
||||
|
||||
|
||||
# Case of initially masked layer !
|
||||
hide_ct = 0
|
||||
|
@ -191,34 +191,34 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
ng_name = f'NG_{ob.name}'
|
||||
ng = context.scene.node_tree.nodes.get(ng_name)
|
||||
if not ng:
|
||||
mess = f'Skip {vl_name}: Not found nodegroup {ng_name}'
|
||||
mess = f'Skip {vl_name}: Not found nodegroup {ng_name}'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
continue
|
||||
|
||||
|
||||
ng_socket = ng.outputs.get(vl_name)
|
||||
|
||||
|
||||
if not ng_socket:
|
||||
mess = f'Skip {vl_name}: Not found in nodegroup {ng_name} sockets'
|
||||
mess = f'Skip {vl_name}: Not found in nodegroup {ng_name} sockets'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
continue
|
||||
|
||||
|
||||
if not len(ng_socket.links):
|
||||
mess = f' socket is disconnected in {ng_name} nodegroup'
|
||||
mess = f' socket is disconnected in {ng_name} nodegroup'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
continue
|
||||
|
||||
fo_node = ng_socket.links[0].to_node
|
||||
fo_socket = ng_socket.links[0].to_socket
|
||||
|
||||
|
||||
if fo_node.type != 'OUTPUT_FILE':
|
||||
mess = f'Skip {vl_name}: node is not an output_file {fo_node.name}'
|
||||
print(mess)
|
||||
messages.append(mess)
|
||||
continue
|
||||
|
||||
|
||||
if fo_node.mute:
|
||||
mess = f'Skip {vl_name}: output is muted {fo_node.name}'
|
||||
print(mess)
|
||||
|
@ -231,12 +231,12 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
subpath = fo_node.file_slots[idx].path
|
||||
fp = Path(fo_node.base_path.rstrip('/')) / subpath
|
||||
fp = Path(bpy.path.abspath(str(fp)).rstrip("/"))
|
||||
|
||||
|
||||
print(f'render {total} layers at: {fp.parent}') #Dbg
|
||||
|
||||
# hide all layer that are: not associated with VL (not in layer_list) or hided initially (store[ob][l])
|
||||
for l in gpl:
|
||||
l.hide = l not in layer_list or store[ob][l]
|
||||
l.hide = l not in layer_list or store[ob][l]
|
||||
|
||||
for l in gpl:
|
||||
if not l.hide:
|
||||
|
@ -250,12 +250,12 @@ class GPEXP_OT_export_as_pdf(bpy.types.Operator):
|
|||
# ob.hide_viewport = False # no need
|
||||
for l, h in layer_dic.items():
|
||||
l.hide = h
|
||||
|
||||
|
||||
for o in selection:
|
||||
o.select_set(True)
|
||||
if act:
|
||||
context.view_layer.objects.active = act
|
||||
|
||||
|
||||
# for oviz in obj_vis:
|
||||
# oviz[0].hide_viewport = oviz[1]
|
||||
|
||||
|
@ -269,7 +269,7 @@ classes=(
|
|||
GPEXP_OT_export_as_pdf,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class GPEXP_OT_render_all_scenes(bpy.types.Operator):
|
|||
if all(x.mute for x in outfiles):
|
||||
print(f'\n -!-> Skip {scn.name}, All output file are muted')
|
||||
continue
|
||||
|
||||
|
||||
print(f'\n --> Rendering {scn.name}')
|
||||
# bpy.context.window.scene = scn
|
||||
bpy.ops.render.render(animation=True, scene=scn.name)
|
||||
|
@ -61,13 +61,13 @@ def scene_render_popup_ui(self, context):
|
|||
scn = bpy.data.scenes.get(si.name)
|
||||
# compare to existing Rlayers (overkill ?)
|
||||
# vls = [scn.view_layers.get(n.layer) for n in rlayers_nodes if scn.view_layers.get(n.layer)]
|
||||
|
||||
|
||||
vls = [vl for vl in scn.view_layers if vl.name != 'View Layer']
|
||||
|
||||
if vls:
|
||||
exclude_count = len([vl for vl in vls if not vl.use])
|
||||
if exclude_count:
|
||||
row.label(text=f'{exclude_count}/{len(vls)} excluded viewlayers', icon='ERROR')
|
||||
row.label(text=f'{exclude_count}/{len(vls)} excluded viewlayers', icon='ERROR')
|
||||
|
||||
if not scn.use_nodes:
|
||||
row.label(text='use_node deactivated', icon='ERROR')
|
||||
|
@ -77,13 +77,13 @@ def scene_render_popup_ui(self, context):
|
|||
if not outfiles:
|
||||
row.label(text='No output files nodes', icon='ERROR')
|
||||
continue
|
||||
|
||||
|
||||
outnum = len(outfiles)
|
||||
muted = len([x for x in outfiles if x.mute])
|
||||
if muted == outnum:
|
||||
row.label(text='All output file are muted', icon='ERROR')
|
||||
continue
|
||||
|
||||
|
||||
elif muted:
|
||||
row.label(text=f'{muted}/{outnum} output file muted', icon='ERROR')
|
||||
continue
|
||||
|
@ -174,7 +174,7 @@ class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator):
|
|||
|
||||
def draw(self, context):
|
||||
scene_render_popup_ui(self, context)
|
||||
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
d = fn.export_crop_to_json()
|
||||
|
@ -182,7 +182,7 @@ class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator):
|
|||
print('No crop to export, border disabled in all scenes')
|
||||
|
||||
platform = sys.platform
|
||||
|
||||
|
||||
blend = Path(bpy.data.filepath)
|
||||
|
||||
scn_to_render = [si.name for si in context.scene.scenes_list if si.select]
|
||||
|
@ -210,11 +210,11 @@ class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator):
|
|||
else: # Unix : point same for each user
|
||||
cmd = f'"{bin_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a'
|
||||
script_text.append(cmd)
|
||||
|
||||
|
||||
|
||||
script_text.append('echo --- END BATCH ---')
|
||||
script_text.append('pause')
|
||||
|
||||
|
||||
with batch_file.open('w') as fd:
|
||||
fd.write('\n'.join(script_text))
|
||||
|
||||
|
@ -230,7 +230,7 @@ GPEXP_OT_render_all_scenes,
|
|||
GPEXP_OT_bg_render_script_selected_scene,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.Scene.scenes_list = bpy.props.CollectionProperty(type=GPEXP_scene_select_prop)
|
||||
|
@ -238,5 +238,5 @@ def register():
|
|||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
|
||||
del bpy.types.Scene.scenes_list
|
|
@ -18,21 +18,21 @@ class GPEXP_OT_render_scene_switch(bpy.types.Operator):
|
|||
if len(scenes) < 2:
|
||||
self.report({'ERROR'},'No other scene to go to')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
if context.scene.name == 'Render':
|
||||
scn = scenes.get('Scene')
|
||||
if not scn: # get the next available scene
|
||||
self.report({'WARNING'},'No scene named "Scene"')
|
||||
slist = [s.name for s in scenes]
|
||||
scn = scenes[(slist.index(bpy.context.scene.name) + 1) % len(scenes)]
|
||||
|
||||
|
||||
else:
|
||||
scn = scenes.get('Render')
|
||||
if not scn:
|
||||
self.report({'ERROR'},'No "Render" scene yet')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
|
||||
self.report({'INFO'},f'Switched to scene "{scn.name}"')
|
||||
bpy.context.window.scene = scn
|
||||
return {"FINISHED"}
|
||||
|
@ -50,7 +50,7 @@ class GPEXP_OT_swap_render_cams(bpy.types.Operator):
|
|||
if not anim_cam or not bg_cam:
|
||||
self.report({'ERROR'}, 'anim_cam or bg_cam is missing')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
cam = context.scene.camera
|
||||
if not cam:
|
||||
context.scene.camera = anim_cam
|
||||
|
@ -105,7 +105,7 @@ GPEXP_OT_swap_render_cams,
|
|||
GPEXP_OT_set_gp_render_workspace,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
|
@ -41,14 +41,14 @@ def check_outname(ob, l):
|
|||
if fo_node.type != 'OUTPUT_FILE':
|
||||
print(f'Skip {vl_name}: node is not an output_file {fo_node.name}')
|
||||
return
|
||||
|
||||
|
||||
# fo_socket.name isn't right, have to iterate in paths
|
||||
idx = [i for i in fo_node.inputs].index(fo_socket)
|
||||
subpath = fo_node.file_slots[idx].path
|
||||
# fp = Path(fo_node.base_path.rstrip('/')) / subpath
|
||||
# fp = Path(bpy.path.abspath(str(fp)).rstrip("/")) # abspath on disk
|
||||
outname = subpath.split('/')[0] # folder name on disk
|
||||
|
||||
|
||||
return outname
|
||||
|
||||
class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
|
||||
|
@ -93,7 +93,7 @@ class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
|
|||
# skip non rendered layers
|
||||
if l.hide:
|
||||
continue
|
||||
|
||||
|
||||
if l.info.startswith('MA_'):
|
||||
# No point in storing information of masking layers...
|
||||
continue
|
||||
|
@ -101,7 +101,7 @@ class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
|
|||
## Can't check viewlayers and final fileout name if Render scene not even created...
|
||||
""" if not l.viewlayer_render or l.viewlayer_render == 'exclude':
|
||||
continue
|
||||
|
||||
|
||||
fo_name = check_outname(o, l) # get name used for output file folder (same in AE)
|
||||
if not fo_name:
|
||||
print(f'! Could not found fileout name for {o.name} > {l.info}')
|
||||
|
@ -112,7 +112,7 @@ class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
|
|||
## Check opacity, blend mode
|
||||
if l.opacity < 1.0:
|
||||
ldic['opacity'] = l.opacity
|
||||
|
||||
|
||||
if l.blend_mode != 'REGULAR':
|
||||
ldic['blend_mode'] = l.blend_mode
|
||||
|
||||
|
@ -143,7 +143,7 @@ class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
|
|||
|
||||
if multi_mask:
|
||||
ldic['masks'] = multi_mask
|
||||
|
||||
|
||||
## add to full dic
|
||||
if ldic:
|
||||
# add source object ? might be usefull to pin point layer
|
||||
|
@ -167,25 +167,25 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
bl_description = "Display state of layer that migh need adjustement"
|
||||
bl_options = {"REGISTER"} # , "UNDO"
|
||||
|
||||
# clear_unused_view_layers :BoolProperty(name="Clear unused view layers",
|
||||
# clear_unused_view_layers :BoolProperty(name="Clear unused view layers",
|
||||
# description="Delete view layer that aren't used in the nodetree anymore",
|
||||
# default=True)
|
||||
|
||||
all_objects : BoolProperty(name='On All Object',
|
||||
default=True, description='On All object, else use selected objects') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
set_full_opacity : BoolProperty(name='Set Full Opacity',
|
||||
default=True, description='Check/Set full opacity') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
set_use_lights : BoolProperty(name='Disable Use Light',
|
||||
default=True, description='Check/Set use lights disabling') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
set_blend_mode : BoolProperty(name='Set Regular Blend Mode',
|
||||
default=True, description='Check/Set blend mode to regular') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
clear_frame_out_of_range : BoolProperty(name='Clear Frames Out Of Scene Range',
|
||||
default=False, description='Delete frames that before scene start and after scene end range\nWith a tolerance of one frame to avoid problem\nAffect all layers)') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
opacity_exclude_list : StringProperty(name='Skip',
|
||||
default='MA', description='Skip prefixes from this list when changing opacity\nSeparate multiple value with a comma (ex: MA,IN)') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
@ -208,7 +208,7 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
layout = self.layout
|
||||
layout.prop(self, 'all_objects')
|
||||
total = len([o for o in context.scene.objects if o.type == 'GPENCIL'])
|
||||
|
||||
|
||||
target_num = total if self.all_objects else len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
||||
layout.label(text=f'{target_num}/{total} targeted GP')
|
||||
|
||||
|
@ -224,7 +224,7 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
layout.prop(self, 'set_blend_mode')
|
||||
# layout.prop(self, 'clear_unused_view_layers')
|
||||
|
||||
def execute(self, context):
|
||||
def execute(self, context):
|
||||
if self.all_objects:
|
||||
pool = [o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||
else:
|
||||
|
@ -235,7 +235,7 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
for ob in pool:
|
||||
changes.append(f'>> {ob.name}')
|
||||
layers = ob.data.layers
|
||||
|
||||
|
||||
if self.clear_frame_out_of_range:
|
||||
ct = fn.clear_frame_out_of_range(ob, verbose=False)
|
||||
if ct:
|
||||
|
@ -243,11 +243,11 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
|
||||
for l in layers:
|
||||
used = False
|
||||
|
||||
|
||||
## mask check
|
||||
# if l.mask_layers:
|
||||
# print(f'-> masks')
|
||||
# state = '' if l.use_mask_layer else ' (disabled)'
|
||||
# state = '' if l.use_mask_layer else ' (disabled)'
|
||||
# print(f'{ob.name} > {l.info}{state}:')
|
||||
# used = True
|
||||
# for ml in l.mask_layers:
|
||||
|
@ -263,7 +263,7 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
print(f'Skipped layer : {l.info}')
|
||||
else:
|
||||
full_opacity_state = '' if self.set_full_opacity else ' (check only)'
|
||||
mess = f'{l.info} : opacity {l.opacity:.2f} >> 1.0{full_opacity_state}'
|
||||
mess = f'{l.info} : opacity {l.opacity:.2f} >> 1.0{full_opacity_state}'
|
||||
print(mess)
|
||||
changes.append(mess)
|
||||
if self.set_full_opacity:
|
||||
|
@ -319,7 +319,7 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
|||
# if not render:
|
||||
# print('SKIP, no Render scene')
|
||||
# return {"CANCELLED"}
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||
|
@ -334,13 +334,13 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
|||
|
||||
all_objects : BoolProperty(name='On All Object',
|
||||
default=True, description='On All object, else use selected objects') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
object_name : BoolProperty(name='Normalize Object Name',
|
||||
default=True, description='Make the object name lowercase') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
layer_name : BoolProperty(name='Normalize Layers Names',
|
||||
default=True, description='Make the layers name lowercase') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
# dash_to_undescore : BoolProperty(name='Dash To Underscore',
|
||||
# default=True, description='Make the layers name lowercase') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
@ -359,7 +359,7 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
|||
gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)])
|
||||
else:
|
||||
gp_ct = len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
||||
|
||||
|
||||
layout.label(text=f'{gp_ct} to lower-case')
|
||||
layout.separator()
|
||||
layout.label(text=f'Choose what to rename:')
|
||||
|
@ -407,10 +407,10 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
|||
|
||||
all_objects : BoolProperty(name='On All GP Object',
|
||||
default=True, description='On All object, else use selected Grease Pencil objects') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
rename_data : BoolProperty(name='Rename Gpencil Data',
|
||||
default=True, description='Rename Also the Grease Pencil data using same name as object') # , options={'SKIP_SAVE'}
|
||||
|
||||
|
||||
delete : BoolProperty(default=False, options={'SKIP_SAVE'})
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
@ -425,7 +425,7 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
|||
o.name = o.name[4:]
|
||||
ct += 1
|
||||
self.report({'INFO'}, f'{ct}/{len(gps)} number prefix removed from object names')
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
@ -437,7 +437,7 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
|||
gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)])
|
||||
else:
|
||||
gp_ct = len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
||||
|
||||
|
||||
layout.prop(self, 'rename_data')
|
||||
layout.label(text=f'{gp_ct} objects to renumber')
|
||||
if not gp_ct:
|
||||
|
@ -459,8 +459,8 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
|||
|
||||
for i, o in reversed_enumerate(pool):
|
||||
if o.show_in_front:
|
||||
fronts.append(pool.pop(i))
|
||||
|
||||
fronts.append(pool.pop(i))
|
||||
|
||||
cam_loc = context.scene.camera.matrix_world.to_translation()
|
||||
|
||||
# filter by distance to camera object (considering origins)
|
||||
|
@ -473,15 +473,15 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
|||
regex_num = re.compile(r'^(\d{3})_')
|
||||
for o in pool:
|
||||
renum = regex_num.search(o.name)
|
||||
|
||||
|
||||
if not renum:
|
||||
o.name = f'{str(ct).zfill(3)}_{o.name}'
|
||||
|
||||
|
||||
else:
|
||||
## either replace or leave untouched
|
||||
# continue
|
||||
o.name = f'{str(ct).zfill(3)}_{o.name[4:]}'
|
||||
|
||||
|
||||
ct += 10
|
||||
if self.rename_data and o.name != o.data.name:
|
||||
o.data.name = o.name
|
||||
|
@ -513,17 +513,17 @@ class GPEXP_OT_check_masks(bpy.types.Operator):
|
|||
if not obj_stat in changes:
|
||||
changes.append(obj_stat)
|
||||
print(obj_stat)
|
||||
|
||||
|
||||
hide_state = ' (hided)' if l.hide else ''
|
||||
text = f' {l.info}{hide_state}:' # :masks:
|
||||
text = f' {l.info}{hide_state}:' # :masks:
|
||||
changes.append(text)
|
||||
print(text)
|
||||
|
||||
|
||||
has_masks = False
|
||||
for ml in l.mask_layers:
|
||||
# 'hide', 'invert', 'name'
|
||||
h = ' hided' if ml.hide else ''
|
||||
i = ' (inverted)' if ml.invert else ''
|
||||
i = ' (inverted)' if ml.invert else ''
|
||||
text = f' - {ml.name}{h}{i}'
|
||||
changes.append(text)
|
||||
print(text)
|
||||
|
@ -532,7 +532,7 @@ class GPEXP_OT_check_masks(bpy.types.Operator):
|
|||
if not has_masks:
|
||||
text = 'No masks!'
|
||||
changes.append(text)
|
||||
print(text)
|
||||
print(text)
|
||||
changes.append('')
|
||||
|
||||
if changes:
|
||||
|
@ -601,10 +601,10 @@ class GPEXP_OT_select_layer_in_comp(bpy.types.Operator):
|
|||
print(f'{l.info} -> Select node {n.name}')
|
||||
selected.append(n.name)
|
||||
n.select = True
|
||||
|
||||
|
||||
if not infos and not selected:
|
||||
self.report({'ERROR'}, 'Nothing selected')
|
||||
return {"CANCELLED"}
|
||||
return {"CANCELLED"}
|
||||
|
||||
infos = infos + [f'-- Selected {len(selected)} nodes --'] + selected
|
||||
fn.show_message_box(_message=infos, _title="Selected viewlayer in compo", _icon='INFO')
|
||||
|
@ -620,7 +620,7 @@ GPEXP_OT_check_masks,
|
|||
GPEXP_OT_select_layer_in_comp,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
|
12
__init__.py
12
__init__.py
|
@ -6,8 +6,8 @@ bl_info = {
|
|||
"blender": (2, 93, 0),
|
||||
"location": "View3D",
|
||||
"warning": "",
|
||||
"doc_url": "https://gitlab.com/autour-de-minuit/blender/gp_render",
|
||||
"tracker_url": "https://gitlab.com/autour-de-minuit/blender/gp_render/-/issues",
|
||||
"doc_url": "https://gitlab.com/autour-de-minuit/blender/gp_render",
|
||||
"tracker_url": "https://gitlab.com/autour-de-minuit/blender/gp_render/-/issues",
|
||||
"category": "Object" }
|
||||
|
||||
|
||||
|
@ -52,7 +52,7 @@ bl_modules = (
|
|||
)
|
||||
|
||||
def update_scene_aa(context, scene):
|
||||
scene_aa(toggle=bpy.context.scene.use_aa)
|
||||
scene_aa(toggle=bpy.context.scene.use_aa)
|
||||
|
||||
import bpy
|
||||
|
||||
|
@ -65,12 +65,12 @@ def register():
|
|||
|
||||
# bpy.types.Scene.pgroup_name = bpy.props.PointerProperty(type = PROJ_PGT_settings)
|
||||
bpy.types.Scene.use_aa = bpy.props.BoolProperty(
|
||||
name='Use Native Anti Aliasing',
|
||||
default=True,
|
||||
name='Use Native Anti Aliasing',
|
||||
default=True,
|
||||
description='\
|
||||
Should be Off only if tree contains a merge_NG or alpha-over-combined renderlayers.\n\
|
||||
Auto-set to Off when using node merge button\n\
|
||||
Toggle: AA settings of and muting AA nested-nodegroup',
|
||||
Toggle: AA settings of and muting AA nested-nodegroup',
|
||||
update=update_scene_aa)
|
||||
|
||||
def unregister():
|
||||
|
|
|
@ -14,7 +14,7 @@ def setup_gp_render_workspace():
|
|||
for space in area.spaces:
|
||||
if space.type == 'VIEW_3D':
|
||||
space.region_3d.view_perspective = 'CAMERA'
|
||||
|
||||
|
||||
def register():
|
||||
bpy.app.handlers.load_post.append(setup_gp_render_workspace)
|
||||
|
||||
|
|
174
fn.py
174
fn.py
|
@ -20,11 +20,11 @@ def is_valid_name(name):
|
|||
|
||||
if name.startswith('.'):
|
||||
return False
|
||||
|
||||
|
||||
## FIXME: /!\ "note" as an exclude word is not good practice, temporary fix
|
||||
if name.lower() == 'note':
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
### -- node basic
|
||||
|
@ -38,7 +38,7 @@ def create_node(type, tree=None, **kargs):
|
|||
node = tree.nodes.new(type)
|
||||
for k,v in kargs.items():
|
||||
setattr(node, k, v)
|
||||
|
||||
|
||||
return node
|
||||
|
||||
def new_aa_node(tree, **kargs):
|
||||
|
@ -62,7 +62,7 @@ def create_aa_nodegroup(tree):
|
|||
|
||||
sep = create_node('CompositorNodeSepRGBA', tree=ngroup, location=(-150,0))
|
||||
comb = create_node('CompositorNodeCombRGBA', tree=ngroup, location=(350,25))
|
||||
|
||||
|
||||
# in AA
|
||||
# ngroup.links.new(comb.outputs[0], ng_out.inputs[0]) # <- connect without out AA
|
||||
aa = new_aa_node(ngroup, location=(-400, 0))
|
||||
|
@ -73,24 +73,24 @@ def create_aa_nodegroup(tree):
|
|||
# ngroup.links.new(ng_in.outputs[0], sep.inputs[0])
|
||||
for i in range(3):
|
||||
ngroup.links.new(sep.outputs[i], comb.inputs[i])
|
||||
|
||||
|
||||
# alpha AA
|
||||
alpha_aa = new_aa_node(ngroup, location=(100,-150))
|
||||
ngroup.links.new(sep.outputs[3], alpha_aa.inputs[0])
|
||||
ngroup.links.new(alpha_aa.outputs[0], comb.inputs[3])
|
||||
|
||||
|
||||
ngroup.links.new(comb.outputs[0], ng_out.inputs[0])
|
||||
|
||||
|
||||
|
||||
|
||||
ng = create_node('CompositorNodeGroup', tree=tree)
|
||||
ng.node_tree = ngroup
|
||||
ng.name = ngroup.name
|
||||
ng.hide=True
|
||||
return ng
|
||||
|
||||
|
||||
## -- object and scene settings
|
||||
|
||||
## -- object and scene settings
|
||||
|
||||
def activate_workspace(name='', context=None):
|
||||
if not name:
|
||||
|
@ -105,11 +105,11 @@ def activate_workspace(name='', context=None):
|
|||
if (wkspace := bpy.data.workspaces.get(name)):
|
||||
context.window.workspace = wkspace
|
||||
return True
|
||||
|
||||
|
||||
# Same name with spaces as underscore
|
||||
dir_name = name.replace(' ', '_')
|
||||
filepath = Path(__file__).parent / 'app_templates' / dir_name / 'startup.blend'
|
||||
|
||||
|
||||
ret = bpy.ops.workspace.append_activate(idname=name, filepath=str(filepath))
|
||||
if ret != {'FINISHED'}:
|
||||
print(f'Could not found {name} at {filepath}')
|
||||
|
@ -119,7 +119,7 @@ def activate_workspace(name='', context=None):
|
|||
|
||||
def copy_settings(obj_a, obj_b):
|
||||
exclusion = ['bl_rna', 'id_data', 'identifier','name_property','rna_type','properties', 'stamp_note_text','use_stamp_note',
|
||||
'settingsFilePath', 'settingsStamp', 'select', 'matrix_local', 'matrix_parent_inverse',
|
||||
'settingsFilePath', 'settingsStamp', 'select', 'matrix_local', 'matrix_parent_inverse',
|
||||
'matrix_basis','location','rotation_euler', 'rotation_quaternion', 'rotation_axis_angle', 'scale']
|
||||
|
||||
for attr in dir(obj_a):
|
||||
|
@ -169,7 +169,7 @@ def set_settings(scene=None, aa=True):
|
|||
'''aa == using native AA, else disable scene AA'''
|
||||
if not scene:
|
||||
scene = bpy.context.scene
|
||||
|
||||
|
||||
# specify scene settings for these kind of render
|
||||
set_scene_aa_settings(scene=scene, aa=aa)
|
||||
|
||||
|
@ -177,7 +177,7 @@ def set_settings(scene=None, aa=True):
|
|||
scene.render.use_compositing = True
|
||||
scene.render.use_sequencer = False
|
||||
scene.view_settings.view_transform = 'Standard'
|
||||
|
||||
|
||||
scene.render.resolution_percentage = 100
|
||||
|
||||
# output (fast write settings since this is just to delete afterwards...)
|
||||
|
@ -192,7 +192,7 @@ def scene_aa(scene=None, toggle=True):
|
|||
scene=bpy.context.scene
|
||||
|
||||
# enable/disable native anti-alias on active scene
|
||||
set_scene_aa_settings(scene=scene, aa=toggle)
|
||||
set_scene_aa_settings(scene=scene, aa=toggle)
|
||||
# mute/unmute AA nodegroups
|
||||
for n in scene.node_tree.nodes:
|
||||
if n.type == 'GROUP' and n.name.startswith('NG_'):
|
||||
|
@ -216,7 +216,7 @@ def new_scene_from(name, src_scn=None, regen=True, crop=True, link_cam=True, lin
|
|||
for attr in ['frame_start', 'frame_end', 'frame_current', 'camera', 'world']:
|
||||
setattr(scn, attr, getattr(src_scn, attr))
|
||||
copy_settings(src_scn.render, scn.render)
|
||||
|
||||
|
||||
## link cameras (and lights ?)
|
||||
for ob in src_scn.objects:
|
||||
if link_cam and ob.type == 'CAMERA':
|
||||
|
@ -226,7 +226,7 @@ def new_scene_from(name, src_scn=None, regen=True, crop=True, link_cam=True, lin
|
|||
|
||||
# set adapted render settings
|
||||
set_settings(scn)
|
||||
|
||||
|
||||
if crop:
|
||||
scn.render.use_border = True
|
||||
scn.render.use_crop_to_border = True
|
||||
|
@ -239,7 +239,7 @@ def get_render_scene():
|
|||
if render_scn:
|
||||
return render_scn
|
||||
|
||||
## -- Create render scene
|
||||
## -- Create render scene
|
||||
current = bpy.context.scene
|
||||
|
||||
## With data
|
||||
|
@ -256,14 +256,14 @@ def get_render_scene():
|
|||
for attr in ['frame_start', 'frame_end', 'frame_current', 'camera', 'world']:
|
||||
setattr(render_scn, attr, getattr(current, attr))
|
||||
copy_settings(current.render, render_scn.render)
|
||||
|
||||
|
||||
## link cameras (and lights ?)
|
||||
for ob in current.objects:
|
||||
if ob.type in ('CAMERA', 'LIGHT'):
|
||||
render_scn.collection.objects.link(ob)
|
||||
|
||||
render_scn.use_nodes = True
|
||||
|
||||
|
||||
## Clear node tree (initial view layer stuff)
|
||||
render_scn.node_tree.nodes.clear()
|
||||
# for n in reversed(render_scn.node_tree.nodes):
|
||||
|
@ -326,15 +326,15 @@ def get_frame_transform(f, node_tree=None):
|
|||
|
||||
childs = [n for n in node_tree.nodes if n.parent == f]
|
||||
# real_locs = [f.location + n.location for n in childs]
|
||||
|
||||
|
||||
xs = [n.location.x for n in childs] + [n.location.x + n.dimensions.x for n in childs]
|
||||
ys = [n.location.y for n in childs] + [n.location.y - n.dimensions.y for n in childs]
|
||||
xs.sort(key=lambda loc: loc) # x val : ascending
|
||||
xs.sort(key=lambda loc: loc) # x val : ascending
|
||||
ys.sort(key=lambda loc: loc) # ascending # , reversed=True) # y val : descending
|
||||
|
||||
|
||||
loc = Vector((min(xs), max(ys)))
|
||||
dim = Vector((max(xs) - min(xs) + 60, max(ys) - min(ys) + 60))
|
||||
|
||||
|
||||
return loc, dim
|
||||
|
||||
|
||||
|
@ -359,7 +359,7 @@ def bbox(f, frames):
|
|||
ys += [loc.y, loc.y - n.dimensions.y] # - (n.dimensions.y/get_dpi_factor())
|
||||
|
||||
|
||||
# margin ~= 30
|
||||
# margin ~= 30
|
||||
# return xs and ys
|
||||
return [min(xs)-30, max(xs)+30], [min(ys)-30, max(ys)+30]
|
||||
|
||||
|
@ -376,15 +376,15 @@ def get_frames_bbox(node_tree):
|
|||
continue
|
||||
# also contains frames
|
||||
frames[n.parent].append(n)
|
||||
|
||||
|
||||
# Dic for bbox coord
|
||||
for f, nodes in frames.items():
|
||||
if f.parent:
|
||||
continue
|
||||
|
||||
|
||||
xs, ys = bbox(f, frames)
|
||||
# xs, ys = bbox(nodes, frames)
|
||||
|
||||
|
||||
## returning: list of corner coords
|
||||
# coords = [
|
||||
# Vector((xs[0], ys[1])),
|
||||
|
@ -411,7 +411,7 @@ def merge_gplayer_viewlayers(ob, act=None, layers=None):
|
|||
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')
|
||||
|
||||
|
@ -433,14 +433,14 @@ def merge_gplayer_viewlayers(ob, act=None, layers=None):
|
|||
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 !!
|
||||
|
@ -453,19 +453,19 @@ def merge_gplayer_viewlayers(ob, act=None, layers=None):
|
|||
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]
|
||||
# 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)
|
||||
rd_scn.view_layers.remove(vl)
|
||||
# if not vl.name in used_vl_name:
|
||||
# rd_scn.view_layers.remove(vl)
|
||||
|
||||
|
@ -478,13 +478,13 @@ def group_adjacent_layer_prefix_rlayer(ob, excluded_prefix=[], first_name=True):
|
|||
|
||||
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])]
|
||||
## Create adjacent grp list: [('CO', [layer1, layer2]), ('LN', [layer3, layer4])]
|
||||
adjacent_prefix_groups = [
|
||||
(g[0], list(g[1])) for g in
|
||||
(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 '')
|
||||
]
|
||||
|
@ -559,7 +559,7 @@ def rearrange_frames(node_tree):
|
|||
## order the dict by frame.y location
|
||||
frame_d = {key: value for key, value in sorted(frame_d.items(), key=lambda pair: pair[1][0].y - pair[1][1].y, reverse=True)}
|
||||
frames = [[f, v[0], v[1].y] for f, v in frame_d.items()] # [frame_node, real_loc, real dimensions]
|
||||
|
||||
|
||||
top = frames[0][1].y # upper node location.y
|
||||
# top = 0 #always start a 0
|
||||
offset = 0
|
||||
|
@ -571,7 +571,7 @@ def rearrange_frames(node_tree):
|
|||
f[0].location.y = (f[1].y - f[0].location.y) - offset # avoid offset when recalculating from 0 top
|
||||
# f[0].location.y = f[1].y - top - offset
|
||||
offset += f[2] + 200 # gap
|
||||
|
||||
|
||||
f[0].update()
|
||||
|
||||
def reorder_inputs(ng):
|
||||
|
@ -657,7 +657,7 @@ def all_connected_forward(n, nlist=[]):
|
|||
else:
|
||||
return nlist
|
||||
else:
|
||||
nlist = all_connected_forward(lnk.to_node, nlist)
|
||||
nlist = all_connected_forward(lnk.to_node, nlist)
|
||||
if n in nlist:
|
||||
return nlist
|
||||
return nlist + [n]
|
||||
|
@ -692,7 +692,7 @@ def reorder_nodegroup_content(ngroup):
|
|||
n_thread = all_connected_forward_from_socket(out)
|
||||
if n_thread:
|
||||
n_threads.append(n_thread)
|
||||
|
||||
|
||||
level = grp_in.location.y
|
||||
for thread in n_threads:
|
||||
top = max([n.location.y for n in thread])
|
||||
|
@ -718,7 +718,7 @@ def clear_nodegroup_content_if_disconnected(ngroup):
|
|||
continue
|
||||
if not connect_to_group_input(n) and not connect_to_group_output(n): # is disconnected from both side
|
||||
ngroup.nodes.remove(n)
|
||||
|
||||
|
||||
reorder_nodegroup_content(ngroup)
|
||||
|
||||
def clean_nodegroup_inputs(ng, skip_existing_pass=True):
|
||||
|
@ -756,7 +756,7 @@ def bridge_reconnect_nodegroup(ng, socket_name=None):
|
|||
ngroup.links.new(sockin, aa.inputs[0])
|
||||
ngroup.links.new(aa.outputs[0], sockout)
|
||||
print(f'{ng.name}: Bridged {sockin.name}')
|
||||
|
||||
|
||||
|
||||
def random_color(alpha=False):
|
||||
import random
|
||||
|
@ -789,7 +789,7 @@ def nodegroup_merge_inputs(ngroup):
|
|||
ao = create_node('CompositorNodeAlphaOver', tree=ngroup, location=(x,y), hide=True)
|
||||
ngroup.links.new(prev.outputs[0], ao.inputs[1])
|
||||
ngroup.links.new(inp, ao.inputs[2])
|
||||
|
||||
|
||||
x += offset_x
|
||||
y += offset_y
|
||||
prev = ao
|
||||
|
@ -798,7 +798,7 @@ def nodegroup_merge_inputs(ngroup):
|
|||
aa = create_aa_nodegroup(ngroup) # new_aa_node(ngroup)
|
||||
aa.location = (ao.location.x + 200, ao.location.y)
|
||||
ngroup.links.new(ao.outputs[0], aa.inputs[0]) # node_tree
|
||||
|
||||
|
||||
# create one input and link
|
||||
out = ngroup.outputs.new('NodeSocketColor', ngroup.inputs[0].name)
|
||||
ngroup.links.new(aa.outputs[0], ng_out.inputs[0])
|
||||
|
@ -852,7 +852,7 @@ def delete_numbering(fo): # padding=3
|
|||
elems = fs.path.split('/')
|
||||
for i, e in enumerate(elems):
|
||||
elems[i] = re.sub(r'^\d{3}_', '', e)
|
||||
|
||||
|
||||
new = '/'.join(elems)
|
||||
fs.path = new
|
||||
|
||||
|
@ -868,7 +868,7 @@ def renumber_keep_existing(fo, offset=10, invert=True):
|
|||
|
||||
if fo.type != 'OUTPUT_FILE': return
|
||||
ct = 10
|
||||
|
||||
|
||||
if invert:
|
||||
reverse_fileout_inputs(fo)
|
||||
|
||||
|
@ -931,7 +931,7 @@ def renumber_keep_existing(fo, offset=10, invert=True):
|
|||
add_fileslot_number(fs, prev_num + offset)
|
||||
else:
|
||||
add_fileslot_number(fs, ct)
|
||||
|
||||
|
||||
else:
|
||||
if prev_num is not None:
|
||||
# iterate rename
|
||||
|
@ -968,7 +968,7 @@ def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_
|
|||
import re
|
||||
|
||||
name = layer.info
|
||||
|
||||
|
||||
pattern = PATTERN
|
||||
sep = '_'
|
||||
res = re.search(pattern, name.strip())
|
||||
|
@ -993,7 +993,7 @@ def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_
|
|||
# tag2 = prefix2.upper().strip() + sep
|
||||
if desc:
|
||||
name = desc
|
||||
|
||||
|
||||
if suffix:
|
||||
if suffix == 'suffixkillcode':
|
||||
sfix = ''
|
||||
|
@ -1013,7 +1013,7 @@ def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_
|
|||
old = layer.info
|
||||
print(f'{old} >> {new}')
|
||||
layer.info = new
|
||||
|
||||
|
||||
# Also change name string in modifier target !
|
||||
for ob in [o for o in bpy.data.objects if o.type == 'GPENCIL' and o.data == layer.id_data]:
|
||||
for m in ob.grease_pencil_modifiers:
|
||||
|
@ -1037,7 +1037,7 @@ def build_layers_targets_from_dopesheet(context):
|
|||
gpl = context.object.data.layers
|
||||
act = gpl.active
|
||||
dopeset = context.space_data.dopesheet
|
||||
|
||||
|
||||
|
||||
if dopeset.show_only_selected:
|
||||
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
||||
|
@ -1048,7 +1048,7 @@ def build_layers_targets_from_dopesheet(context):
|
|||
|
||||
layer_pool = [l for o in pool for l in o.data.layers]
|
||||
layer_pool = list(set(layer_pool)) # remove dupli-layers from same data source with
|
||||
|
||||
|
||||
# apply search filter
|
||||
if dopeset.filter_text:
|
||||
layer_pool = [l for l in layer_pool if (dopeset.filter_text.lower() in l.info.lower()) ^ dopeset.use_filter_invert]
|
||||
|
@ -1067,7 +1067,7 @@ def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
|||
self.layout.label(text=l)
|
||||
else:
|
||||
self.layout.label(text=l[0], icon=l[1])
|
||||
|
||||
|
||||
if isinstance(_message, str):
|
||||
_message = [_message]
|
||||
bpy.context.window_manager.popup_menu(draw, title = _title, icon = _icon)
|
||||
|
@ -1092,7 +1092,7 @@ def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
|||
elif len(l) == 3: # ops
|
||||
self.layout.operator_context = "INVOKE_DEFAULT"
|
||||
self.layout.operator(l[0], text=l[1], icon=l[2], emboss=False) # <- highligh the entry
|
||||
|
||||
|
||||
if isinstance(_message, str):
|
||||
_message = [_message]
|
||||
bpy.context.window_manager.popup_menu(draw, title = _title, icon = _icon)
|
||||
|
@ -1122,15 +1122,15 @@ def is_render_included(o, scn):
|
|||
|
||||
|
||||
def get_crop_pixel_coord(scn):
|
||||
# width height probably not needed. might need
|
||||
# width height probably not needed. might need
|
||||
px_width = (scn.render.border_max_x - scn.render.border_min_x) * scn.render.resolution_x
|
||||
px_height = (scn.render.border_max_y - scn.render.border_min_y) * scn.render.resolution_y
|
||||
|
||||
|
||||
pos_x = (scn.render.border_min_x + ((scn.render.border_max_x - scn.render.border_min_x) / 2)) * scn.render.resolution_x
|
||||
|
||||
## coord y > image center coord from bottom-left (Blender)
|
||||
# pos_y = (scn.render.border_min_y + ((scn.render.border_max_y - scn.render.border_min_y) / 2)) * scn.render.resolution_y,
|
||||
|
||||
|
||||
## image center coord from top-left (AE)
|
||||
pos_y = ((1 - scn.render.border_max_y) + ((scn.render.border_max_y - scn.render.border_min_y) / 2)) * scn.render.resolution_y
|
||||
|
||||
|
@ -1145,15 +1145,15 @@ def get_crop_pixel_coord(scn):
|
|||
def export_crop_to_json():
|
||||
'''Export crop to json coords for AE
|
||||
'''
|
||||
|
||||
|
||||
blend = Path(bpy.data.filepath)
|
||||
json_path = blend.parent / 'render' / f'{blend.stem}.json' #f'{ob.name}.json'
|
||||
|
||||
## per scene : json_path = Path(bpy.data.filepath).parent / 'render' / f'{scn.name}.json'
|
||||
# json_path = Path(bpy.data.filepath).parent / 'render' / f'{scn.name}.json' #f'{ob.name}.json'
|
||||
|
||||
|
||||
coord_dic = {}
|
||||
|
||||
|
||||
for scn in bpy.data.scenes:
|
||||
# if scn.name in {'Scene', 'Render'}:
|
||||
# if scn.name == 'Scene':
|
||||
|
@ -1178,7 +1178,7 @@ def export_crop_to_json():
|
|||
# save bbox
|
||||
with json_path.open('w') as fd:
|
||||
json.dump(coord_dic, fd, indent='\t')
|
||||
|
||||
|
||||
print(f'Coords saved at: {json_path}')
|
||||
return coord_dic
|
||||
|
||||
|
@ -1189,13 +1189,13 @@ def set_border_region_from_coord(coords, scn=None, margin=30, export_json=True):
|
|||
'''
|
||||
|
||||
scn = scn or bpy.context.scene
|
||||
|
||||
|
||||
coords2d_x = sorted([c[0] for c in coords])
|
||||
coords2d_y = sorted([c[1] for c in coords])
|
||||
|
||||
margin_width = margin / scn.render.resolution_x
|
||||
margin_height = margin / scn.render.resolution_y
|
||||
|
||||
|
||||
# set crop
|
||||
scn.render.border_min_x = coords2d_x[0] - margin_width
|
||||
scn.render.border_max_x = coords2d_x[-1] + margin_width
|
||||
|
@ -1223,17 +1223,17 @@ def set_border_region_from_coord(coords, scn=None, margin=30, export_json=True):
|
|||
|
||||
def get_gp_box_all_frame(ob, cam=None):
|
||||
'''set crop to object bounding box considering whole animation. Cam should not be animated (render in bg_cam)
|
||||
return 2d bbox in pixels
|
||||
return 2d bbox in pixels
|
||||
'''
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
coords_cam_list = []
|
||||
scn = bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
start = time()
|
||||
|
||||
|
||||
if ob.animation_data and ob.animation_data.action: # use frame set on all frames
|
||||
print(f'{ob.name} has anim')
|
||||
# frame_nums = sorted(list(set([f.frame_number for l in ob.data.layers if len(l.frames) for f in l.frames if len(f.strokes) and scn.frame_start <= f.frame_number <= scn.frame_end])))
|
||||
# frame_nums = sorted(list(set([f.frame_number for l in ob.data.layers if len(l.frames) for f in l.frames if len(f.strokes) and scn.frame_start <= f.frame_number <= scn.frame_end])))
|
||||
for num in range(scn.frame_start, scn.frame_end+1):
|
||||
scn.frame_set(num)
|
||||
for l in ob.data.layers:
|
||||
|
@ -1257,7 +1257,7 @@ def get_gp_box_all_frame(ob, cam=None):
|
|||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
|
||||
|
||||
print(time() - start) # Dbg-time
|
||||
return coords_cam_list
|
||||
|
||||
|
@ -1276,7 +1276,7 @@ def has_keyframe(ob, attr):
|
|||
def get_gp_box_all_frame_selection(oblist=None, scn=None, cam=None, timeout=40):
|
||||
'''
|
||||
get points of all selection
|
||||
return 2d bbox in pixels
|
||||
return 2d bbox in pixels
|
||||
return None if timeout (too long to process, better to do it visually)
|
||||
'''
|
||||
|
||||
|
@ -1289,7 +1289,7 @@ def get_gp_box_all_frame_selection(oblist=None, scn=None, cam=None, timeout=40):
|
|||
|
||||
cam = cam or scn.camera
|
||||
start = time()
|
||||
|
||||
|
||||
if any(has_anim(ob) for ob in oblist):
|
||||
print(f'at least one is animated: {oblist}')
|
||||
for num in range(scn.frame_start, scn.frame_end+1):
|
||||
|
@ -1304,7 +1304,7 @@ def get_gp_box_all_frame_selection(oblist=None, scn=None, cam=None, timeout=40):
|
|||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
|
||||
|
||||
if time() - t0 > timeout:
|
||||
print(f'timeout (more than {timeout}s to calculate) evaluating frame position of objects {oblist}')
|
||||
return
|
||||
|
@ -1325,16 +1325,16 @@ def get_gp_box_all_frame_selection(oblist=None, scn=None, cam=None, timeout=40):
|
|||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
|
||||
|
||||
|
||||
|
||||
print(f'{len(coords_cam_list)} gp points listed {time() - start:.1f}s')
|
||||
return coords_cam_list
|
||||
|
||||
def get_bbox_2d(ob, cam=None):
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
scn = bpy.context.scene
|
||||
scn = bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
coords2d = [world_to_camera_view(scn, cam, p) for p in get_bbox_3d(ob)]
|
||||
coords2d = [world_to_camera_view(scn, cam, p) for p in get_bbox_3d(ob)]
|
||||
coords2d_x = sorted([c[0] for c in coords2d])
|
||||
coords2d_y = sorted([c[1] for c in coords2d])
|
||||
|
||||
|
@ -1348,9 +1348,9 @@ def get_bbox_2d(ob, cam=None):
|
|||
return [Vector(b) for b in bbox2d_coords]
|
||||
|
||||
def set_box_from_selected_objects(scn=None, cam=None, export_json=False):
|
||||
scn = scn or bpy.context.scene
|
||||
scn = scn or bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
|
||||
|
||||
selection = [o for o in scn.objects if o.select_get()] # selected_objects
|
||||
coords = get_gp_box_all_frame_selection(oblist=selection, scn=scn, cam=cam)
|
||||
if not coords:
|
||||
|
@ -1445,10 +1445,10 @@ def get_collection_childs_recursive(col, cols=[], include_root=True):
|
|||
cols.append(sub)
|
||||
if len(sub.children):
|
||||
cols = get_collection_childs_recursive(sub, cols)
|
||||
|
||||
|
||||
if include_root and col not in cols: # add root col
|
||||
cols.append(col)
|
||||
|
||||
|
||||
return cols
|
||||
|
||||
def unlink_objects_from_scene(oblist, scn):
|
||||
|
@ -1473,7 +1473,7 @@ def split_object_to_scene():
|
|||
active = bpy.context.object
|
||||
scene_name = active.name
|
||||
objs = [o for o in bpy.context.selected_objects]
|
||||
|
||||
|
||||
if bpy.data.scenes.get(scene_name):
|
||||
print(f'Scene "{scene_name}" Already Exists')
|
||||
raise Exception(f'Scene "{scene_name}" Already Exists')
|
||||
|
@ -1497,7 +1497,7 @@ def split_object_to_scene():
|
|||
continue
|
||||
if sob not in objs:
|
||||
col.objects.unlink(sob)
|
||||
|
||||
|
||||
frame_names = [n.label for n in new.node_tree.nodes if n.type == 'FRAME' if new.objects.get(n.label)]
|
||||
remove_scene_nodes_by_obj_names(new, frame_names, negative=True)
|
||||
|
||||
|
@ -1511,7 +1511,7 @@ def split_object_to_scene():
|
|||
|
||||
## remove asset from original scene
|
||||
#src_frame_names = [n.label for n in src.node_tree.nodes if n.type == 'FRAME' and n.label in [o.name for o in objs]]
|
||||
#remove_scene_nodes_by_obj_names(src, src_frame_names)
|
||||
#remove_scene_nodes_by_obj_names(src, src_frame_names)
|
||||
remove_scene_nodes_by_obj_names(src, frame_names, negative=False)
|
||||
|
||||
# unlink objects ?
|
||||
|
@ -1522,7 +1522,7 @@ def split_object_to_scene():
|
|||
coords = get_gp_box_all_frame_selection(oblist=gp_objs, scn=new, cam=new.camera)
|
||||
if not coords:
|
||||
return f'Scene "{scene_name}" created. But Border was not set (Timeout during GP analysis), should be done by hand if needed then use export crop to json'
|
||||
|
||||
|
||||
set_border_region_from_coord(coords, margin=30, scn=new, export_json=True)
|
||||
export_crop_to_json()
|
||||
|
||||
|
@ -1548,7 +1548,7 @@ def clear_frame_out_of_range(o, verbose=False):
|
|||
print(f'del: obj {o.name} > layer {l.info} > frame {f.frame_number}')
|
||||
l.frames.remove(f)
|
||||
ct += 1
|
||||
|
||||
|
||||
# before
|
||||
elif f.frame_number < scn.frame_start - 1:
|
||||
if first:
|
||||
|
@ -1596,7 +1596,7 @@ def set_scene_output_from_active_fileout_item():
|
|||
|
||||
excluded = ['file_format', 'color_mode', 'color_depth',
|
||||
'view_settings', 'views_format']
|
||||
|
||||
|
||||
''' ## all attrs
|
||||
# 'cineon_black', 'cineon_gamma', 'cineon_white',
|
||||
# 'color_depth', 'color_mode', 'compression', 'display_settings',
|
||||
|
@ -1641,7 +1641,7 @@ def set_layer_colors(skip_if_colored=False):
|
|||
continue
|
||||
print(l.info, '->', color)
|
||||
l.channel_color = color
|
||||
|
||||
|
||||
bpy.context.preferences.edit.use_anim_channel_group_colors = True
|
||||
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@ def add_rlayer(layer_name, scene=None, location=None, color=None, node_name=None
|
|||
node_name = layer_name # 'RL_' +
|
||||
|
||||
if not scene:
|
||||
scene=bpy.context.scene
|
||||
|
||||
scene=bpy.context.scene
|
||||
|
||||
nodes = scene.node_tree.nodes
|
||||
|
||||
|
||||
comp = nodes.get(node_name)
|
||||
if comp:
|
||||
if comp.layer == node_name:
|
||||
|
@ -33,7 +33,7 @@ def add_rlayer(layer_name, scene=None, location=None, color=None, node_name=None
|
|||
comp.location = location
|
||||
if color:
|
||||
comp.color = color
|
||||
|
||||
|
||||
if width:
|
||||
comp.width = width
|
||||
comp.show_preview = False
|
||||
|
@ -51,14 +51,14 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
vl_name = rlayer.layer
|
||||
if not vl_name or vl_name == 'View Layer':
|
||||
print(f'Bad layer for node {rlayer.name}')
|
||||
|
||||
|
||||
if not ' / ' in vl_name:
|
||||
print(f'no slash (" / ") separator in vl_name {vl_name}, should be "obj.name / layer_name"')
|
||||
return
|
||||
|
||||
obname, lname = vl_name.split(' / ')
|
||||
lname = bpy.path.clean_name(lname)
|
||||
|
||||
|
||||
if not frame:
|
||||
if rlayer.parent:
|
||||
frame=rlayer.parent
|
||||
|
@ -96,7 +96,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
|
||||
ng_in = fn.create_node('NodeGroupInput', tree=ngroup, location=(-600,0))
|
||||
ng_out = fn.create_node('NodeGroupOutput', tree=ngroup, location=(600,0))
|
||||
|
||||
|
||||
else:
|
||||
ngroup = ng.node_tree
|
||||
ng_in = ngroup.nodes.get('Group Input')
|
||||
|
@ -113,20 +113,20 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
|
||||
## get nodes from frame
|
||||
# rl_nodes = [n for n in nodes if n.type == 'R_LAYERS' and n.layer != 'View Layer' and n.parent == frame]
|
||||
|
||||
|
||||
# auto clean : if an input exists but is not linked and name not exists in rlayers of current frame
|
||||
for s in reversed(ng.inputs):
|
||||
if not s.is_linked: # and not any(x.layer == s.name for x in rl_nodes)
|
||||
print(f'removing grp unlinked input {s.name}')
|
||||
ng.inputs.remove(s)
|
||||
|
||||
|
||||
## get nodes from linked NG inputs ??? maybe more clear...
|
||||
# rl_nodes = [s.links[0].from_node for s in ng.inputs if s.links and s.links[0].from_node and s.links[0].from_node.type == 'R_LAYERS']
|
||||
|
||||
|
||||
## reorder
|
||||
fn.reorder_inputs(ng)
|
||||
ng.update()
|
||||
|
||||
|
||||
# CREATE NG outsocket (individual, without taking merge)
|
||||
connected = False
|
||||
|
||||
|
@ -136,7 +136,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
if socket:
|
||||
connected = True
|
||||
groupout = ng.outputs.get(socket.name)
|
||||
|
||||
|
||||
ng.update()
|
||||
|
||||
if not connected:
|
||||
|
@ -151,7 +151,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
# ng_in.outputs[vl_name]
|
||||
ngroup.links.new(ng_in.outputs[vl_name], aa.inputs[0]) # node_tree
|
||||
ngroup.links.new(aa.outputs[0], ng_out.inputs[vl_name]) # node_tree
|
||||
|
||||
|
||||
aa.mute = scene.use_aa # mute if native AA is used
|
||||
|
||||
|
||||
|
@ -166,7 +166,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
ng.update()
|
||||
# reorder output to match inputs
|
||||
fn.reorder_outputs(ng)
|
||||
|
||||
|
||||
ng.update()
|
||||
|
||||
# Clear : delete orphan nodes that are not connected from ng_in
|
||||
|
@ -180,7 +180,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
if groupout.links and groupout.links[0].to_node.type == 'OUTPUT_FILE':
|
||||
# if already connected to outfile just skip cause user might have customised the name
|
||||
return
|
||||
|
||||
|
||||
slot_name = f'{lname}/{lname}_'
|
||||
out_name = f'OUT_{obname}' # or get output from frame
|
||||
if not out:
|
||||
|
@ -194,7 +194,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
|||
out.base_path = f'//render/{bpy.path.clean_name(obname)}'
|
||||
|
||||
## out_input = out.inputs.get(slot_name) # ok for non-numbered outputs
|
||||
|
||||
|
||||
out_input = None
|
||||
out_input = fn.get_numbered_output(out, slot_name)
|
||||
|
||||
|
@ -235,9 +235,9 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
# ob.data = ob.data.copy() # create duplicate (this will also affect the one in original scene !!!)
|
||||
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
|
||||
|
@ -269,7 +269,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
|
||||
if rlayer_list: # rlayer exists
|
||||
print(f'{len(rlayer_list)} nodes using {vl_name}')
|
||||
|
||||
|
||||
# affect only the one within an object frame
|
||||
framed_rl = [n for n in rlayer_list if n.parent and n.parent.label == ob.name]
|
||||
if framed_rl:
|
||||
|
@ -282,7 +282,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
cp.select = True # select so the user see that it existed
|
||||
return vl, cp
|
||||
|
||||
# Returned if existed and OK
|
||||
# Returned if existed and OK
|
||||
|
||||
if not ob.name in frame_dic.keys(): # and len(frame_dic[ob.name])
|
||||
print(f'\n{ob.name} -> {l.info} (first generation)')
|
||||
|
@ -303,7 +303,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
frame.label = ob.name
|
||||
frame.label_size = 50
|
||||
frame.location = (loc[0], loc[1] + 20)
|
||||
|
||||
|
||||
cp = add_rlayer(vl_name, scene=scene, location=loc)
|
||||
cp.parent = frame
|
||||
# use same color as layer
|
||||
|
@ -326,7 +326,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
if cp.layer != vl_name:
|
||||
print(f'problem with {cp}: {cp.layer} != {vl_name}')
|
||||
return
|
||||
|
||||
|
||||
if fn.has_channel_color(l):
|
||||
cp.use_custom_color = True
|
||||
cp.color = l.channel_color
|
||||
|
@ -339,16 +339,16 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
top_loc = fn.real_loc(rl_nodes[0])
|
||||
else:
|
||||
top_loc = fn.get_frame_transform(frame[1], node_tree) - 60
|
||||
|
||||
|
||||
# cp.location = (top_loc[0], top_loc[1] + 100) # temp location to adjust x loc
|
||||
|
||||
# list of layer names in nodes order
|
||||
rl_names = [n.layer.split(' / ')[1] for n in rl_nodes] # get True layer name from rl
|
||||
# names with the right order WITH the new layer included
|
||||
names = [lay.info for lay in ob.data.layers if lay.info in rl_names or lay == l]
|
||||
|
||||
|
||||
rl_nodes.append(cp)
|
||||
|
||||
|
||||
# filter by getting index(layer_name)
|
||||
cp.parent = frame
|
||||
rl_nodes.sort(key=lambda x : names.index(x.layer.split(' / ')[1])) # Sort True layer name from rl
|
||||
|
@ -356,13 +356,13 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
|||
offset = 0
|
||||
# print(f'number of nodes in frame: {len(rl_nodes)}')
|
||||
ref_node = rl_nodes[0]
|
||||
|
||||
|
||||
# print('ref_node: ', ref_node.name, ref_node.location)
|
||||
for n in rl_nodes:
|
||||
# set x loc from first node in list (maybe use leftmost ?)
|
||||
n.location = Vector((fn.real_loc(ref_node)[0], top_loc[1] - offset)) - n.parent.location
|
||||
offset += 180
|
||||
|
||||
|
||||
n.update()
|
||||
# reorder render layers nodes within frame
|
||||
|
||||
|
@ -379,12 +379,12 @@ def export_gp_objects(oblist, exclude_list=[], scene=None):
|
|||
oblist = [oblist]
|
||||
if isinstance(exclude_list, str):
|
||||
exclude_list = [p.strip() for p in exclude_list.split(',')]
|
||||
|
||||
|
||||
# print('exclude_list: ', exclude_list)
|
||||
|
||||
for ob in oblist:
|
||||
for l in ob.data.layers:
|
||||
# if l.hide:
|
||||
# if l.hide:
|
||||
# continue
|
||||
if l.hide or any(x + '_' in l.info for x in exclude_list): # exclude hided ?
|
||||
print(f'Exclude export: {ob.name} : {l.info}')
|
||||
|
|
4
prefs.py
4
prefs.py
|
@ -7,7 +7,7 @@ class gp_render_prefs(bpy.types.AddonPreferences):
|
|||
# name='Resample on the fly',
|
||||
# description="Allow smoother stroke when using pinch\nnote that stroke using textured materials will not be resampled",
|
||||
# default=True)
|
||||
|
||||
|
||||
advanced : bpy.props.BoolProperty(
|
||||
name='Advanced Options', # Reproject On Guessed Plane
|
||||
description="Display advanced options",
|
||||
|
@ -22,7 +22,7 @@ def get_addon_prefs():
|
|||
function to read current addon preferences properties
|
||||
access with : get_addon_prefs().super_special_option
|
||||
'''
|
||||
import os
|
||||
import os
|
||||
addon_name = os.path.splitext(__name__)[0]
|
||||
preferences = bpy.context.preferences
|
||||
addon_prefs = preferences.addons[addon_name].preferences
|
||||
|
|
|
@ -23,7 +23,7 @@ scn = bpy.context.scene
|
|||
# - Import Fx3D (or render from Fx3D file... maybe easier consifering the number)
|
||||
|
||||
|
||||
## tried to make color that fit in White theme
|
||||
## tried to make color that fit in White theme
|
||||
## (difficult for readability since this text color is not the same)
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ def set_layer_colors():
|
|||
continue
|
||||
print(l.info, '->', color)
|
||||
l.channel_color = color
|
||||
|
||||
|
||||
C.preferences.edit.use_anim_channel_group_colors = True
|
||||
|
||||
|
||||
|
|
48
ui.py
48
ui.py
|
@ -17,26 +17,26 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
advanced = prefs.advanced
|
||||
layout = self.layout
|
||||
layout.operator('gp.render_scene_switch', icon='SCENE_DATA', text='Switch Scene')
|
||||
|
||||
|
||||
scn = context.scene
|
||||
|
||||
|
||||
## Camera swapping
|
||||
row = layout.row()
|
||||
cam = scn.camera
|
||||
if cam:
|
||||
text = f'{cam.name} : {scn.render.resolution_x}x{scn.render.resolution_y}' # Cam:
|
||||
text = f'{cam.name} : {scn.render.resolution_x}x{scn.render.resolution_y}' # Cam:
|
||||
else:
|
||||
text = f'None' # Cam:
|
||||
text = f'None' # Cam:
|
||||
|
||||
# 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 !')
|
||||
|
@ -45,21 +45,21 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
|
||||
if not scn.use_nodes or not scn.node_tree:
|
||||
return
|
||||
|
||||
|
||||
disabled_output = [n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.mute]
|
||||
if disabled_output:
|
||||
output_ct = len([n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE'])
|
||||
layout.label(text=f'{len(disabled_output)}/{output_ct} Output Muted', icon='INFO')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
||||
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)
|
||||
# row=col.row(align=True)
|
||||
row=layout.row(align=True)
|
||||
|
||||
|
||||
row1 = row.row(align=True)
|
||||
row1.operator('gp.activate_only_selected_layers', text=f'Activate Only {ct} Layer Nodes')
|
||||
row1.enabled = ct > 0
|
||||
|
@ -76,7 +76,7 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
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()
|
||||
|
@ -87,18 +87,18 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
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:')
|
||||
row=layout.row()
|
||||
row.operator('gp.mute_toggle_output_nodes', icon='NODE_INSERT_ON', text='Mute').mute = True
|
||||
row.operator('gp.mute_toggle_output_nodes', icon='NODE_INSERT_OFF', text='Unmute').mute = False
|
||||
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
||||
col=layout.column()
|
||||
col.label(text='Clean and updates:')
|
||||
|
||||
|
@ -119,7 +119,7 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
subcol.operator('gp.number_outputs', icon='LINENUMBERS_ON', text=txt).mode = 'SELECTED'
|
||||
# subcol.operator('gp.normalize_outnames', icon='SYNTAX_OFF', text=f'Normalize Paths {ct} Selected Ouptut') # not ready
|
||||
# col.operator('gp.number_outputs', icon='LINENUMBERS_ON', text='Renumber all outputs').mode = 'ALL'
|
||||
|
||||
|
||||
if advanced:
|
||||
subcol.operator('gp.set_output_node_format', icon='OUTPUT', text='Copy Active Output Format')
|
||||
subcol.operator('gp.set_active_fileout_to_compout', icon='OUTPUT', text='Active Slot to Composite')
|
||||
|
@ -204,7 +204,7 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
|||
# merge layers from dopesheet
|
||||
row.operator('gp.merge_viewlayers_to_active', text=txt, icon='SELECT_EXTEND')
|
||||
row.enabled= ct > 1
|
||||
|
||||
|
||||
col.operator('gpexp.auto_merge_adjacent_prefix', icon='SELECT_EXTEND')
|
||||
|
||||
## all and objects
|
||||
|
@ -227,17 +227,17 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
|||
col.operator('gp.export_infos_for_compo', icon='FILE', text='Export Layers Infos') # Not really need, called in Check layers invoke
|
||||
col.operator('gp.layers_state', icon='CHECKMARK', text='Check layers')
|
||||
col.operator('gp.check_masks', icon='MOD_MASK', text='Has Masks')
|
||||
|
||||
|
||||
# row = layout.row()
|
||||
layout.prop(bpy.context.preferences.edit, 'use_anim_channel_group_colors')
|
||||
|
||||
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.operator('gp.export_as_pdf', icon='RENDER_STILL', text='Render All to PDF Sequences')
|
||||
if bpy.app.version < (3,0,0):
|
||||
if bpy.app.version < (3,0,0):
|
||||
row.label(text='Not Blender 3.0.0+')
|
||||
|
||||
|
||||
## Append GP Render workspace (usefull for user with disabled 'load_UI')
|
||||
if not bpy.data.workspaces.get('GP Render'):
|
||||
layout.operator('gp.set_gp_render_workspace')
|
||||
|
@ -274,7 +274,7 @@ def viewlayer_layout(layout, scn):
|
|||
# bl_label = "View Layers"
|
||||
# def draw(self, context):
|
||||
# layout = self.layout
|
||||
# viewlayer_layout(layout, context)
|
||||
# viewlayer_layout(layout, context)
|
||||
|
||||
class GPEXP_PT_viewlayers_ui(Panel):
|
||||
bl_space_type = "NODE_EDITOR"
|
||||
|
@ -361,7 +361,7 @@ GPEXP_PT_gp_node_ui,
|
|||
GPEXP_PT_gp_dopesheet_ui,
|
||||
)
|
||||
|
||||
def register():
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
# bpy.types.DATA_PT_gpencil_layers.prepend(manager_ui)
|
||||
|
|
Loading…
Reference in New Issue