diff --git a/CHANGELOG.md b/CHANGELOG.md index 4edbe6a..562bb2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,17 @@ Activate / deactivate layer opaticty according to prefix Activate / deactivate all masks using MA layers --> +0.9.0 + +- feat: Renumber objects prefix according to origin point depth, and button to remove +- ui: improve dopesheet panel readability + 0.8.0 - 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 + - 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 0.7.0 diff --git a/OP_setup_layers.py b/OP_setup_layers.py index a57ea6d..24f52d7 100644 --- a/OP_setup_layers.py +++ b/OP_setup_layers.py @@ -5,6 +5,8 @@ from bpy.props import (FloatProperty, StringProperty, IntProperty) from . import fn +import math +import re class GPEXP_OT_layers_state(bpy.types.Operator): bl_idname = "gp.layers_state" @@ -221,9 +223,103 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator): return {"FINISHED"} +class GPEXP_OT_auto_number_object(bpy.types.Operator): + bl_idname = "gp.auto_number_object" + bl_label = "Auto Number Object" + bl_description = "Automatic prefix number based on origin distance to camera and in_front values\nCtrl + Clic to delete name to delete numbering" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'GPENCIL' + + all_objects : BoolProperty(name='On All GP Object', + default=False, 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): + # if event.alt: + # self.all_objects=True + if event.ctrl or self.delete: + regex_num = re.compile(r'^(\d{3})_') + ct = 0 + gps = [o for o in context.selected_objects if o.type == 'GPENCIL'] + for o in gps: + if regex_num.match(o.name): + 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) + + def draw(self, context): + layout = self.layout + layout.prop(self, 'all_objects') + if self.all_objects: + gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL']) + 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: + layout.label(text='No Gpencil object to renumber', icon = 'ERROR') + + 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'] + + def reversed_enumerate(collection: list): + for i in range(len(collection)-1, -1, -1): + yield i, collection[i] + + fronts = [] + + ## separate In Front objects: + + for i, o in reversed_enumerate(pool): + if o.show_in_front: + fronts.append(pool.pop(i)) + + cam_loc = context.scene.camera.matrix_world.to_translation() + + # filter by distance to camera object (considering origins) + pool.sort(key=lambda x: math.dist(x.matrix_world.to_translation(), cam_loc)) + fronts.sort(key=lambda x: math.dist(x.matrix_world.to_translation(), cam_loc)) + # re-insert fitlered infront object before others + pool = fronts + pool + + ct = 10 + 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 + + return {"FINISHED"} + classes=( GPEXP_OT_layers_state, -GPEXP_OT_lower_layers_name +GPEXP_OT_lower_layers_name, +GPEXP_OT_auto_number_object ) def register(): diff --git a/__init__.py b/__init__.py index 9536398..c69bd53 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, 8, 0), + "version": (0, 9, 0), "blender": (2, 93, 0), "location": "View3D", "warning": "", diff --git a/ui.py b/ui.py index ea72717..2c00ee6 100644 --- a/ui.py +++ b/ui.py @@ -178,15 +178,16 @@ class GPEXP_PT_gp_dopesheet_ui(Panel): layout.label(text=f'(Active dopesheet layer not in active obj)') ## On layers + col = layout.column() 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) + col.operator('gp.add_layer_to_render', icon='RENDERLAYERS', text=txt) # merge (only accessible if multiple layers selected) - row = layout.row() + 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 @@ -197,16 +198,21 @@ class GPEXP_PT_gp_dopesheet_ui(Panel): ## all and objects layout.separator() - layout.label(text='Whole objects:') + 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' - 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' + 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() - - layout.operator('gp.layers_state', icon='CHECKMARK', text='Check layers') - layout.operator('gp.lower_layers_name', icon='SYNTAX_OFF', text='Rename Lowercase') + 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.layers_state', icon='CHECKMARK', text='Check layers') + col.operator('gp.lower_layers_name', icon='SYNTAX_OFF', text='Rename Lowercase') # row = layout.row() layout.prop(bpy.context.preferences.edit, 'use_anim_channel_group_colors')