From 364b45e473ea31f24cb32447cb93cfe61c930450 Mon Sep 17 00:00:00 2001 From: Pullusb Date: Fri, 17 Sep 2021 16:31:26 +0200 Subject: [PATCH] gp layers state check and set 0.2.7 - feat: check layer states (check use light, opacity, blend mode) and correct if needed - ui: added chennel group color switch - feat: added color from active layer on merge ops --- CHANGELOG.md | 6 ++ OP_check_layer_status.py | 9 ++- OP_merge_layers.py | 16 +++-- OP_setup_layers.py | 134 +++++++++++++++++++++++++++++++++++++++ __init__.py | 8 ++- fn.py | 16 ++++- ui.py | 9 ++- 7 files changed, 186 insertions(+), 12 deletions(-) create mode 100644 OP_setup_layers.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 00671e9..e2d6a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ Activate / deactivate layer opaticty according to prefix Activate / deactivate all masks using MA layers --> +0.2.7 + +- feat: check layer states (check use light, opacity, blend mode) and correct if needed +- ui: added chennel group color switch +- feat: added color from active layer on merge ops + 0.2.6 - ui: name changes diff --git a/OP_check_layer_status.py b/OP_check_layer_status.py index 0a79cfb..1835e56 100644 --- a/OP_check_layer_status.py +++ b/OP_check_layer_status.py @@ -1,9 +1,7 @@ import bpy from . import fn -## direct use (Pop up menu version below) - - +## not used, replaced by "setup_layers.py" class GPEXP_OT_check_layers_state(bpy.types.Operator): bl_idname = "gp.check_layers_state" bl_label = "Check Layers State" @@ -21,6 +19,7 @@ class GPEXP_OT_check_layers_state(bpy.types.Operator): def invoke(self, context, event): self.ctrl=event.ctrl self.alt=event.alt + return self.execute(context) return context.window_manager.invoke_props_dialog(self) def draw(self, context): @@ -53,8 +52,8 @@ class GPEXP_OT_check_layers_state(bpy.types.Operator): print(f'-> opacity {l.opacity}') used = True - if l.use_light: - print(f'-> use light !') + if l.use_lights: + print(f'-> use lights !') used = True if l.blend_mode != 'REGULAR': print(f'-> blend mode "{l.blend_mode}" !') diff --git a/OP_merge_layers.py b/OP_merge_layers.py index 6be2529..fb9f5a4 100644 --- a/OP_merge_layers.py +++ b/OP_merge_layers.py @@ -1,9 +1,13 @@ import bpy import re +from math import isclose from . import fn from . import gen_vlayer -def merge_layers(rlayers, obname=None, active=None, disconnect=True): +# TODO : make a merge compatible with already merged nodegroup (or even other node type) +# --> need to delete/mute AA internal node + +def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None): print(f'Merging {len(rlayers)} layers') print('->', [r.layer for r in rlayers]) @@ -41,7 +45,8 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True): # change colors of those nodes disconnected_groups = [] - color = fn.random_color() + if not color: + color = fn.random_color() for n in rlayers: n.use_custom_color = True n.color = color @@ -160,8 +165,11 @@ class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator): nodes.active = rl # make it active so the merge use this one rlayers.append(rl) - - merge_layers(rlayers, disconnect=self.disconnect) + + color = None + if not any(isclose(i, 0.2, abs_tol=0.0001) for i in act.channel_color): # and bpy.context.preferences.edit.use_anim_channel_group_colors + color = act.channel_color + merge_layers(rlayers, disconnect=self.disconnect, color=color) return {"FINISHED"} diff --git a/OP_setup_layers.py b/OP_setup_layers.py new file mode 100644 index 0000000..0a5071c --- /dev/null +++ b/OP_setup_layers.py @@ -0,0 +1,134 @@ +import bpy +from bpy.props import (FloatProperty, + BoolProperty, + EnumProperty, + StringProperty, + IntProperty) +from . import fn + +class GPEXP_OT_layers_state(bpy.types.Operator): + bl_idname = "gp.layers_state" + bl_label = "Set Layers State" + bl_description = "Display state of layer that migh need adjustement" + bl_options = {"REGISTER"} # , "UNDO" + + # clear_unused_view_layers :BoolProperty(name="Clear unused view layers", + # description="Delete view layer that aren't used in the nodetree anymore", + # default=True) + + # TODO : (optional) export layer opacity to json and/or text + # (that way compo artists can re-affect opacity quickly or at least have a reminder) + + all_objects : BoolProperty(name='On All Object', + default=False, 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'} + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'GPENCIL' + + def invoke(self, context, event): + # self.ctrl=event.ctrl + # self.alt=event.alt + if event.alt: + self.all_objects=True + # return self.execute(context) + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + layout = self.layout + layout.prop(self, 'all_objects') + layout.separator() + layout.label(text='Set (or only perform a check):') + layout.prop(self, 'set_full_opacity') + layout.prop(self, 'set_use_lights') + layout.prop(self, 'set_blend_mode') + # layout.prop(self, 'clear_unused_view_layers') + + def execute(self, context): + if self.all_objects: + pool = [o for o in context.scene.objects if o.type == 'GPENCIL'] + else: + pool = [o for o in context.selected_objects if o.type == 'GPENCIL'] + # pool = [context.object] + + changes = [] + for ob in pool: + changes.append(f'>> {ob.name}') + layers = ob.data.layers + for l in layers: + used = False + + ## mask check + # if l.mask_layers: + # print(f'-> masks') + # state = '' if l.use_mask_layer else ' (disabled)' + # print(f'{ob.name} > {l.info}{state}:') + # used = True + # for ml in l.mask_layers: + # mlstate = ' (disabled)' if ml.hide else '' + # mlinvert = ' <>' if ml.invert else '' + # print(f'{ml.info}{mlstate}{mlinvert}') + + if l.opacity != 1: + + full_opacity_state = '' if self.set_full_opacity else ' (check only)' + mess = f'{l.info} : opacity {l.opacity:.2f} >> 1.0{full_opacity_state}' + print(mess) + changes.append(mess) + if self.set_full_opacity: + l.opacity = 1.0 + used = True + + if l.use_lights: + + use_lights_state = '' if self.set_use_lights else ' (check only)' + mess = f'{l.info} : disable use lights{use_lights_state}' + print(mess) + changes.append(mess) + if self.set_use_lights: + l.use_lights = False + used = True + + if l.blend_mode != 'REGULAR': + blend_mode_state = '' if self.set_blend_mode else ' (check only)' + mess = f'{l.info} : blend mode "{l.blend_mode}" >> regular{blend_mode_state}' + print(mess) + changes.append(mess) + if self.set_blend_mode: + l.blend_mode = 'REGULAR' + used = True + + if used: + print() + if changes: + changes.append('') + + fn.show_message_box(_message=changes, _title="Layers Check Report", _icon='INFO') + + # render = bpy.data.scenes.get('Render') + # if not render: + # print('SKIP, no Render scene') + # return {"CANCELLED"} + + return {"FINISHED"} + +classes=( +GPEXP_OT_layers_state, +) + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) \ No newline at end of file diff --git a/__init__.py b/__init__.py index 80d5a7e..1590486 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ bl_info = { "name": "GP Render", "description": "Organise export of gp layers through compositor output", "author": "Samuel Bernou", - "version": (0, 2, 6), + "version": (0, 2, 7), "blender": (2, 93, 0), "location": "View3D", "warning": "", @@ -18,6 +18,8 @@ from . import OP_connect_toggle from . import OP_manage_outputs from . import OP_number_outputs from . import OP_scene_switch +# from . import OP_check_layer_status +from . import OP_setup_layers from . import ui import bpy @@ -34,6 +36,8 @@ def register(): OP_manage_outputs.register() OP_number_outputs.register() OP_scene_switch.register() + # OP_check_layer_status.register() + OP_setup_layers.register() ui.register() # bpy.types.Scene.pgroup_name = bpy.props.PointerProperty(type = PROJ_PGT_settings) @@ -42,6 +46,8 @@ def unregister(): return ui.unregister() + OP_setup_layers.unregister() + # OP_check_layer_status.unregister() OP_scene_switch.unregister() OP_number_outputs.unregister() OP_manage_outputs.unregister() diff --git a/fn.py b/fn.py index 02c50d0..aea40c9 100644 --- a/fn.py +++ b/fn.py @@ -520,4 +520,18 @@ def renumber_keep_existing(fo, offset=10): # first check if it has a number (if not bas) prev = fs - ct += offset \ No newline at end of file + ct += offset + + +## confirm pop-up message: +def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'): + def draw(self, context): + for l in _message: + if isinstance(l, str): + 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) \ No newline at end of file diff --git a/ui.py b/ui.py index 949f88b..ac2d255 100644 --- a/ui.py +++ b/ui.py @@ -67,7 +67,6 @@ class GPEXP_PT_gp_node_ui(Panel): 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.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' @@ -112,12 +111,20 @@ class GPEXP_PT_gp_dopesheet_ui(Panel): ## all and objects layout.separator() + layout.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' layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt).mode='SELECTED' layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All Visible GP To Render').mode='ALL' + layout.separator() + + layout.operator('gp.layers_state', icon='CHECKMARK', text='Check layers') + + # row = layout.row() + layout.prop(bpy.context.preferences.edit, 'use_anim_channel_group_colors') + class GPEXP_MT_multi_user_doc(bpy.types.Menu): # bl_idname = "OBJECT_MT_custom_menu"