1.0.2 - added: GP object with name starting with `.` are ignored from "all objects" operation (renaming, numbering, sending to render scene) - temporarily, layer named `note` (case insensitive) are ignored as well. This should be removed in later version to keep only dot exculsion rule.
367 lines
15 KiB
Python
367 lines
15 KiB
Python
import bpy
|
|
from bpy.types import Panel
|
|
from .prefs import get_addon_prefs
|
|
# from .preferences import get_addon_prefs
|
|
|
|
# Node view panel
|
|
class GPEXP_PT_gp_node_ui(Panel):
|
|
bl_space_type = "NODE_EDITOR"
|
|
bl_region_type = "UI"
|
|
# bl_category = "View"
|
|
bl_category = "GP render"
|
|
bl_label = "Gpencil Render Manager"
|
|
|
|
def draw(self, context):
|
|
prefs = get_addon_prefs()
|
|
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:
|
|
else:
|
|
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 !')
|
|
layout.operator('gp.enable_all_viewlayers', text=f'Reactivate {exclude_count} Excluded View Layers')
|
|
|
|
|
|
if not scn.use_nodes or not scn.node_tree:
|
|
return
|
|
|
|
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
|
|
|
|
row2=row.row(align=True)
|
|
row2.operator("wm.call_panel", text="", icon='RENDERLAYERS').name = "GPEXP_PT_viewlayers_ui"
|
|
row2.operator("wm.call_panel", text="", icon='SCENE_DATA').name = "GPEXP_PT_viewlayers_multi_ui"
|
|
# row2.operator("gp.viewlayer_popup", text="", icon='RENDERLAYERS') # Ops invoke hack
|
|
# row2.operator("wm.call_menu", text="", icon='RENDERLAYERS').name = "GPEXP_MT_viewlayers_popup" # Bad menu
|
|
|
|
if advanced:
|
|
col = layout.column(align=True)
|
|
txt = f'Merge {ct} Layer Nodes'
|
|
col.operator('gp.merge_selected_viewlayer_nodes', icon='NODETREE', text=txt).disconnect = True
|
|
col.operator('gp.merge_selected_viewlayer_nodes', icon='NODETREE', text='Merge (keep connect)').disconnect = False
|
|
col.enabled = ct > 1
|
|
|
|
layout.separator()
|
|
col = layout.column()
|
|
subcol = col.column()
|
|
n = context.scene.node_tree.nodes.active
|
|
if n:
|
|
subcol.enabled = n and n.type == 'R_LAYERS' and not n.outputs[0].is_linked
|
|
else:
|
|
subcol.enabled = False
|
|
|
|
subcol.operator('gp.reconnect_render_layer', icon='ANIM', text=f'Reconnect {ct} Layer Node')
|
|
|
|
col.operator('gp.delete_render_layer', icon='TRACKING_CLEAR_FORWARDS', text=f'Delete {ct} Layer Node')
|
|
|
|
layout.separator()
|
|
|
|
layout.label(text='All Outputs:')
|
|
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:')
|
|
|
|
|
|
col.separator()
|
|
|
|
col.operator('gp.clean_compo_tree', icon='BRUSHES_ALL', text='Clean Nodes') # NODE_CORNER
|
|
col.operator('gp.reset_render_settings', icon='SCENE', text='Reset All Scenes Render Settings')
|
|
col.operator('gp.check_render_scene', icon='PRESET', text='Check For Problems')
|
|
|
|
col.separator()
|
|
|
|
## (re)number exports
|
|
ct = len([n for n in context.scene.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.select])
|
|
txt = f'Renumber {ct} Selected Outputs'
|
|
subcol = col.column()
|
|
subcol.enabled = bool(ct)
|
|
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')
|
|
|
|
|
|
|
|
layout.separator()
|
|
|
|
col=layout.column()
|
|
col.label(text='Delete Options:')
|
|
if advanced:
|
|
col.operator('gp.clear_render_tree', icon='X', text='Clear Framed Nodes')
|
|
col.operator('gp.clear_render_tree', icon='X', text='Clear & Delete Render Scene').mode = "COMPLETE"
|
|
|
|
layout.separator()
|
|
layout.label(text='Scenes:')
|
|
layout.operator('gp.split_to_scene', icon='DUPLICATE', text='Split Selected Obj To Scene')
|
|
|
|
row = layout.row(align=True)
|
|
row.operator('gp.set_crop_from_selection', icon='CON_OBJECTSOLVER', text='Autoset Crop')
|
|
row.operator('gp.export_crop_coord_to_json', icon='FILE', text='Export json')
|
|
|
|
layout.label(text='Render:')
|
|
row = layout.row(align=True)
|
|
row.operator('gp.render_selected_scenes', icon='RENDER_ANIMATION', text='Render Selected Scene')
|
|
row.operator('gp.bg_render_script_selected_scenes', icon='TEXT', text='Gen Batch')
|
|
# row.operator('gp.render_all_scenes', icon='RENDER_ANIMATION', text='Render All')
|
|
|
|
if advanced:
|
|
layout.separator()
|
|
col = layout.column()
|
|
col.label(text='Post-Render:')
|
|
col.operator('gp.renumber_files_on_disk', icon='FILE', text='Renumber Files On Disk')
|
|
|
|
layout.prop(context.scene, 'use_aa', text='Use Native AA Settings')
|
|
layout.prop(prefs, 'advanced', text='Show Advanced Options')
|
|
# layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='Layer To Render').mode = 'ALL'
|
|
# layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='Layer To Render').mode = 'SELECTED'
|
|
|
|
# layout.operator('gp.merge_layers', icon='X', text='Merge selected nodes')
|
|
|
|
class GPEXP_PT_gp_dopesheet_ui(Panel):
|
|
bl_space_type = 'DOPESHEET_EDITOR'
|
|
bl_region_type = 'UI'
|
|
bl_category = "GP Render"
|
|
# bl_parent_id='DOPESHEET_PT_gpencil_mode'
|
|
bl_label = "Gpencil Render Manager"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'GPENCIL'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
## TODO: add auto-build
|
|
# layout.operator('gp_export.render_auto_build')
|
|
if context.object:
|
|
layout.label(text=f'Object: {context.object.name}')
|
|
if context.object.data.users > 1:
|
|
row = layout.row()
|
|
row.label(text=f'Multiple users ({context.object.data.users})', icon='ERROR')
|
|
row.operator("wm.call_menu", text="", icon='QUESTION').name = "GPEXP_MT_multi_user_doc"
|
|
if context.object.data.layers.active:
|
|
layout.label(text=f'viewlayer: {context.object.data.layers.active.viewlayer_render}')
|
|
else:
|
|
layout.label(text=f'No active layer found')
|
|
layout.label(text=f'(Active dopesheet layer not in active obj)')
|
|
|
|
## On layers
|
|
col = layout.column()
|
|
col.operator('gp.select_layer_in_comp', icon='RESTRICT_SELECT_OFF', text='Select Nodes')
|
|
if context.object and context.object.type == 'GPENCIL':
|
|
txt = f'{len([l for l in context.object.data.layers if l.select])} Layer(s) To Render'
|
|
else:
|
|
txt = 'Layer To Render'
|
|
col.operator('gp.add_layer_to_render', icon='RENDERLAYERS', text=txt)
|
|
|
|
|
|
# merge (only accessible if multiple layers selected)
|
|
row = col.row()
|
|
ct = len([l for l in context.object.data.layers if l.select])
|
|
txt = f'Merge {ct} layers'
|
|
# merge layers from dopesheet
|
|
row.operator('gp.merge_viewlayers_to_active', text=txt, icon='SELECT_EXTEND')
|
|
row.enabled= ct > 1
|
|
|
|
## all and objects
|
|
layout.separator()
|
|
|
|
col = layout.column()
|
|
col.label(text='Whole Objects:')
|
|
if context.scene.name != 'Render':
|
|
txt = f'{len([o for o in context.selected_objects if o.type == "GPENCIL" and o.select_get()])} Selected Object(s) To Render'
|
|
col.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt).mode='SELECTED'
|
|
col.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All Visible GP To Render').mode='ALL'
|
|
|
|
layout.separator()
|
|
col = layout.column()
|
|
col.label(text='Fixes:')
|
|
row = col.row(align=True)
|
|
row.operator('gp.auto_number_object', icon='OBJECT_DATAMODE', text='Renumber Objects')
|
|
row.operator('gp.auto_number_object', icon='X', text='').delete = True
|
|
col.operator('gp.lower_layers_name', icon='SYNTAX_OFF', text='Rename Lowercase')
|
|
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):
|
|
row.label(text='Not Blender 3.0.0')
|
|
|
|
|
|
class GPEXP_MT_multi_user_doc(bpy.types.Menu):
|
|
bl_label = "Case of multiuser objects"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
# call another menu
|
|
#layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
|
|
#**Behavior from context mode**
|
|
col = layout.column()
|
|
col.label(text='Multi user data will be rendered all together on last generated viewlayers.', icon='INFO')
|
|
col.label(text='Make them single user if needed to render separately.')
|
|
# col.label(text='Select objects > call search pop-up (F3) > "Make single user" > tick "object data" > ok')
|
|
col.label(text='Procedure:')
|
|
|
|
col.label(text='- select concerned objects')
|
|
col.label(text='- call search pop-up (F3)')
|
|
col.label(text='- search "Make single user"')
|
|
col.label(text='- tick only "object data"')
|
|
|
|
|
|
def viewlayer_layout(layout, scn):
|
|
for vl in scn.view_layers:
|
|
row = layout.row()
|
|
row.prop(vl, 'use', text=vl.name, icon='RESTRICT_RENDER_OFF' if vl.use else 'RESTRICT_RENDER_ON', emboss=False, toggle=0)
|
|
# row.prop(vl, 'use', text=vl.name, icon='RESTRICT_RENDER_OFF', emboss=False)
|
|
|
|
## Can only toggle one with a menu
|
|
# class GPEXP_MT_viewlayers_popup(bpy.types.Menu):
|
|
# bl_label = "View Layers"
|
|
# def draw(self, context):
|
|
# layout = self.layout
|
|
# viewlayer_layout(layout, context)
|
|
|
|
class GPEXP_PT_viewlayers_ui(Panel):
|
|
bl_space_type = "NODE_EDITOR"
|
|
bl_region_type = "UI"
|
|
bl_label = "View Layers"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.label(text=f'{context.scene.name} :: View layers')
|
|
col = layout.column(align=True)
|
|
viewlayer_layout(col, context.scene)
|
|
|
|
class GPEXP_PT_viewlayers_multi_ui(Panel):
|
|
bl_space_type = "NODE_EDITOR"
|
|
bl_region_type = "UI"
|
|
bl_label = "Multi View Layers"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.label(text=f'{len(bpy.data.scenes)} scenes :: View layers')
|
|
for s in bpy.data.scenes:
|
|
col = layout.column()
|
|
col.label(text=f'{s.name}::')
|
|
viewlayer_layout(col, s)
|
|
layout.separator()
|
|
|
|
class GPEXP_OT_viewlayer_popup_invoke(bpy.types.Operator):
|
|
bl_idname = "gp.viewlayer_popup"
|
|
bl_label = "Viewlayer Popup"
|
|
bl_description = "Pop up menu for toggling viewlayers"
|
|
bl_options = {"REGISTER",}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
viewlayer_layout(layout, context)
|
|
|
|
def execute(self, context):
|
|
return {"FINISHED"}
|
|
|
|
|
|
## not registered for now (better to place render menu in GP dopesheet)
|
|
def manager_ui(self, context):
|
|
'''appended to DATA_PT_gpencil_layers'''
|
|
|
|
layout = self.layout
|
|
|
|
## On layers
|
|
if context.object and context.object.type == 'GPENCIL':
|
|
txt = f'{len([l for l in context.object.data.layers if l.select])} Layer(s) To Render'
|
|
else:
|
|
txt = 'Layer To Render'
|
|
layout.operator('gp.add_layer_to_render', icon='RENDERLAYERS', text=txt)
|
|
|
|
## On objects
|
|
# txt = 'Selected Object To Render'
|
|
if context.scene.name != 'Render':
|
|
txt = f'{len([o for o in context.selected_objects if o.type == "GPENCIL" and o.select_get()])} Selected Object(s) To Render'
|
|
layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt).mode='SELECTED'
|
|
layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All GP at once').mode='ALL'
|
|
|
|
|
|
# ## function to append in a menu
|
|
# def palette_manager_menu(self, context):
|
|
# """Palette menu to append in existing menu"""
|
|
# # GPENCIL_MT_material_context_menu
|
|
# layout = self.layout
|
|
# # {'EDIT_GPENCIL', 'PAINT_GPENCIL','SCULPT_GPENCIL','WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}
|
|
# layout.separator()
|
|
# prefs = get_addon_prefs()
|
|
|
|
# layout.operator("", text='do stuff from material submenu', icon='MATERIAL')
|
|
|
|
#-# REGISTER
|
|
|
|
classes=(
|
|
GPEXP_PT_viewlayers_ui,
|
|
GPEXP_PT_viewlayers_multi_ui,
|
|
GPEXP_OT_viewlayer_popup_invoke,
|
|
GPEXP_MT_multi_user_doc,
|
|
GPEXP_PT_gp_node_ui,
|
|
GPEXP_PT_gp_dopesheet_ui,
|
|
)
|
|
|
|
def register():
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
# bpy.types.DATA_PT_gpencil_layers.prepend(manager_ui)
|
|
|
|
def unregister():
|
|
# bpy.types.DATA_PT_gpencil_layers.remove(manager_ui)
|
|
for cls in reversed(classes):
|
|
bpy.utils.unregister_class(cls) |