diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bf3ca8..64eb99e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ Activate / deactivate layer opacity according to prefix Activate / deactivate all masks using MA layers --> +1.1.3 + +- added: clean material stack in auto-build + 1.1.2 - added: popup panel with options for autobuild diff --git a/OP_auto_build.py b/OP_auto_build.py index 4da6887..6aacdf3 100644 --- a/OP_auto_build.py +++ b/OP_auto_build.py @@ -64,8 +64,11 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator): 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, + 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', + 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, @@ -98,6 +101,7 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator): layout = self.layout col = layout.column() col.prop(self, 'clean_name_and_visibility') + col.prop(self, 'clean_material_duplication') row = col.row() row.prop(self, 'prefix_filter') row.active = self.clean_name_and_visibility @@ -129,9 +133,10 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator): 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 [o for o in context.scene.objects if o.type == 'GPENCIL']: + for o in all_gp_objects: if o.hide_render: print(f'skip: {o.name} hide render') continue @@ -145,8 +150,13 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator): if res.group(1) in prefix_to_render and l.hide == True: print(f'{o.name} -> {l.info} : Switch visibility On') l.hide = False - - ob_list = [o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)] + + if self.clean_material_duplication: + print('Clean material duplicates') + for ob in all_gp_objects: + fn.clean_mats_duplication(ob) + + ob_list = [o for o in all_gp_objects if not o.hide_get() and fn.is_valid_name(o.name)] if not ob_list: self.report({'ERROR'}, 'No GP object to render found') return {'CANCELLED'} diff --git a/__init__.py b/__init__.py index e3bff65..40d3144 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": (1, 1, 2), + "version": (1, 1, 3), "blender": (2, 93, 0), "location": "View3D", "warning": "", diff --git a/fn.py b/fn.py index 34f5ff6..dfdb776 100644 --- a/fn.py +++ b/fn.py @@ -1576,4 +1576,64 @@ def set_layer_colors(skip_if_colored=False): print(l.info, '->', color) l.channel_color = color - bpy.context.preferences.edit.use_anim_channel_group_colors = True \ No newline at end of file + bpy.context.preferences.edit.use_anim_channel_group_colors = True + + +def different_gp_mat(mata, matb): + '''return None if no difference (False), string describing color difference (True)''' + a = mata.grease_pencil + b = matb.grease_pencil + if a.color[:] != b.color[:]: + return f'{mata.name} and {matb.name} stroke color is different' + if a.fill_color[:] != b.fill_color[:]: + return f'{mata.name} and {matb.name} fill_color color is different' + if a.show_stroke != b.show_stroke: + return f'{mata.name} and {matb.name} stroke has different state' + if a.show_fill != b.show_fill: + return f'{mata.name} and {matb.name} fill has different state' + +## Clean dups +def clean_mats_duplication(ob, skip_different_materials=True): + '''Clean object material stack of duplication + if a material is named "mat.001" and a "mat" exists, replace with the one with original name + + :skip_different_materials: Don't replace a "mat.???" if orignal "mat" has different color + ''' + + import re + diff_ct = 0 + todel = [] + if ob.type != 'GPENCIL': + return + if not hasattr(ob, 'material_slots'): + return + for i, ms in enumerate(ob.material_slots): + mat = ms.material + if not mat: + continue + match = re.search(r'(.*)\.\d{3}$', mat.name) + if not match: + continue + basemat = bpy.data.materials.get(match.group(1)) + if not basemat: + continue + diff = different_gp_mat(mat, basemat) + if diff: + print(f'! {ob.name} : {diff}') + diff_ct += 1 + if skip_different_materials: + continue + + if mat not in todel: + todel.append(mat) + ms.material = basemat + print(f'{ob.name} : slot {i} >> replaced {mat.name}') + mat.use_fake_user = False + + ### delete (only when using on all objects loop, else can delete another objects mat...) + ## for m in reversed(todel): + ## bpy.data.materials.remove(m) + + if diff_ct: + print(f'{diff_ct} mat skipped >> same name but different color settings!') + # return ('INFO', f'{diff_ct} mat skipped >> same name but different color settings!') \ No newline at end of file