import bpy, re from . import fn def check_broken_modifier_target(pool=None, reports=None): if not reports: reports = [] if not pool: pool = [o for o in bpy.context.scene.objects if o.type == 'GPENCIL'] for o in pool: lay_name_list = [l.info for l in o.data.layers] for m in o.grease_pencil_modifiers: if not hasattr(m, 'layer'): continue if not m.layer: continue if not m.layer in lay_name_list: reports.append(f'Broken modifier target :{o.name} > {m.name} > {m.layer}') # else: # print(f'Modifier target :{o.name} > {m.name} > ok') return reports def check_layer_state(pool=None, reports=None): if not reports: reports = [] if not pool: pool = [o for o in bpy.context.scene.objects if o.type == 'GPENCIL'] for ob in pool: layers = ob.data.layers for l in layers: # if l.mask_layers: # if not any(not x.hide for x in l.mask_layers): # # all masks disable # pass ## 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 '' # mlinvert = ' <>' if ml.invert else '' # reports.append(f' - {ml.name}{mlstate}{mlinvert}') if l.opacity != 1 and not l.info.startswith('MA_'): reports.append(f'{ob.name} > {l.info} > opacity {l.opacity}') # if l.use_lights: # reports.append(f'-> use lights !') if l.blend_mode != 'REGULAR': reports.append(f'{ob.name} > {l.info} > blend mode "{l.blend_mode}" !') return reports def check_file_output_numbering(reports=None): if not reports: reports = [] prenum = re.compile(r'\d{3}_') file_outs = [] for S in bpy.data.scenes: if not S.node_tree or not S.use_nodes: # S.name == 'Scene' or continue file_outs += [n for n in S.node_tree.nodes if n.type == 'OUTPUT_FILE'] if not file_outs: reports.append('No file output nodes found') return reports for fo in file_outs: ### Check for object prefix number in path split_path = fo.base_path.split('/') if not prenum.match(split_path[-1]): report_missing_number = True # No prefix-number in tail part name if len(split_path) >= 2 and prenum.match(split_path[-2]): # report if no prefix-number on parent path part either report_missing_number = False if report_missing_number: reports.append(f'No object numbering : node {fo.name}') pct = 0 if fo.format.file_format == 'OPEN_EXR_MULTILAYER': ## multilayer use layer_slots > slot.name slots = fo.layer_slots for fs in slots: if not prenum.match(fs.name.split('/')[0]): pct += 1 else: ## classic use file_slots > path slots = fo.file_slots for fs in slots: if not prenum.match(fs.path.split('/')[0]): pct += 1 if pct: reports.append(f'{pct}/{len(slots)} slots not numbered: node {fo.name}') return reports class GPEXP_OT_check_render_scene(bpy.types.Operator): bl_idname = "gp.check_render_scene" bl_label = "Check render scene" bl_description = "Auto check render scene" bl_options = {"REGISTER"} # , "UNDO" # 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) @classmethod def poll(cls, context): return True def invoke(self, context, event): return self.execute(context) # return context.window_manager.invoke_props_dialog(self) # def draw(self, context): # layout = self.layout # # layout.prop(self, 'clear_unused_view_layers') def execute(self, context): reports = [] # check gp modifiers broken_mods = check_broken_modifier_target() if broken_mods: reports.append('GP modifiers targets:') reports += broken_mods # check layers layer_state = check_layer_state() if layer_state: if reports: reports.append('') reports.append('Layers State:') reports += layer_state # check file output numbering numbering_problems = check_file_output_numbering() if numbering_problems: if reports: reports.append('') reports.append('File output numbering:') reports += numbering_problems if not reports: self.report({'INFO'}, 'All OK !') reports.append('Everything Ok !') if hasattr(bpy.types, 'GP_OT_file_checker'): # propose to run toolbox checker if exists reports.append(['gp.file_checker', 'Run GP_toolbox File Checker', 'SCENE_DATA']) fn.show_message_box(_message=reports, _title='Potential Problems list') return {"FINISHED"} classes=( GPEXP_OT_check_render_scene, ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls)