diff --git a/CHANGELOG.md b/CHANGELOG.md index 650c23b..161ff33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,13 @@ Activate / deactivate layer opaticty according to prefix Activate / deactivate all masks using MA layers --> +0.6.4 + +- ui: render selected scene has hints on popup panekl like gen batch +- changed: always re-export crop info when using render all scene and generate batch +- changed: batch file has minutes at the end of the name +- fix: windows dynamic batch problem + 0.6.3 - fix: show in ui when there is an active dopesheet layer that is not in active object diff --git a/OP_crop_to_object.py b/OP_crop_to_object.py index 6ca5ece..e715275 100644 --- a/OP_crop_to_object.py +++ b/OP_crop_to_object.py @@ -37,7 +37,9 @@ class GPEXP_OT_export_crop_coord_to_json(bpy.types.Operator): # if not scn.render.use_border or not scn.render.use_crop_to_border: # self.report({'ERROR'}, 'Current scene have cropping disabled or use crop_to_border disabled!') # return {'CANCELLED'} - fn.export_crop_to_json() + crop_dic = fn.export_crop_to_json() + if not crop_dic: + self.report({'ERROR'}, 'No crop to export (Border might be deactivated in all scenes)') return {"FINISHED"} diff --git a/OP_render_scenes.py b/OP_render_scenes.py index 2b1398a..e9cce1b 100644 --- a/OP_render_scenes.py +++ b/OP_render_scenes.py @@ -47,6 +47,47 @@ class GPEXP_OT_render_all_scenes(bpy.types.Operator): class GPEXP_scene_select_prop(PropertyGroup): name : StringProperty() select: BoolProperty() + + +def scene_render_popup_ui(self, context): + layout = self.layout + col = layout.column() + for si in context.scene.scenes_list: + row = col.row() + row.prop(si, 'select',text='') + row.label(text=si.name) + + ## Display warnings + scn = bpy.data.scenes.get(si.name) + # compare to existing Rlayers (overkill ?) + # vls = [scn.view_layers.get(n.layer) for n in rlayers_nodes if scn.view_layers.get(n.layer)] + + vls = [vl for vl in scn.view_layers if vl.name != 'View Layer'] + + if vls: + exclude_count = len([vl for vl in vls if not vl.use]) + if exclude_count: + row.label(text=f'{exclude_count}/{len(vls)} excluded viewlayers', icon='ERROR') + + if not scn.use_nodes: + row.label(text='use_node deactivated', icon='ERROR') + continue + + outfiles = [n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE'] + if not outfiles: + row.label(text='No output files nodes', icon='ERROR') + continue + + outnum = len(outfiles) + muted = len([x for x in outfiles if x.mute]) + if muted == outnum: + row.label(text='All output file are muted', icon='ERROR') + continue + + elif muted: + row.label(text=f'{muted}/{outnum} output file muted', icon='ERROR') + continue + class GPEXP_OT_render_selected_scene(bpy.types.Operator): bl_idname = "gp.render_selected_scenes" bl_label = "Render Selected Scenes" @@ -55,9 +96,12 @@ class GPEXP_OT_render_selected_scene(bpy.types.Operator): @classmethod def poll(cls, context): - return True + return bpy.data.is_saved def invoke(self, context, event): + # if not bpy.data.is_saved: + # self.report({'ERROR'}, 'File needs to be saved') + # return {'CANCELLED'} context.scene.scenes_list.clear() for s in bpy.data.scenes: scn_item = context.scene.scenes_list.add() @@ -67,16 +111,22 @@ class GPEXP_OT_render_selected_scene(bpy.types.Operator): return context.window_manager.invoke_props_dialog(self, width=250) def draw(self, context): - layout = self.layout - col = layout.column() - for si in context.scene.scenes_list: - row = col.row() - row.label(text=si.name) - row.prop(si, 'select',text='') + ## Basic (without hints) + # layout = self.layout + # col = layout.column() + # for si in context.scene.scenes_list: + # row = col.row() + # row.label(text=si.name) + # row.prop(si, 'select',text='') + + scene_render_popup_ui(self, context) def execute(self, context): + d = fn.export_crop_to_json() + if not d: + print('No crop to export, border disabled in all scenes') + scn_to_render = [si.name for si in context.scene.scenes_list if si.select] - start = time() ct = 0 for scn_name in scn_to_render: @@ -111,7 +161,7 @@ class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator): @classmethod def poll(cls, context): - return True + return bpy.data.is_saved def invoke(self, context, event): context.scene.scenes_list.clear() @@ -123,52 +173,20 @@ class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator): return context.window_manager.invoke_props_dialog(self, width=500) def draw(self, context): - layout = self.layout - col = layout.column() - for si in context.scene.scenes_list: - row = col.row() - row.prop(si, 'select',text='') - row.label(text=si.name) - - ## Display warnings - scn = bpy.data.scenes.get(si.name) - # compare to existing Rlayers (overkill ?) - # vls = [scn.view_layers.get(n.layer) for n in rlayers_nodes if scn.view_layers.get(n.layer)] - - vls = [vl for vl in scn.view_layers if vl.name != 'View Layer'] - - if vls: - exclude_count = len([vl for vl in vls if not vl.use]) - if exclude_count: - row.label(text=f'{exclude_count}/{len(vls)} excluded viewlayers', icon='ERROR') - - if not scn.use_nodes: - row.label(text='use_node deactivated', icon='ERROR') - continue - - outfiles = [n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE'] - if not outfiles: - row.label(text='No output files nodes', icon='ERROR') - continue - - outnum = len(outfiles) - muted = len([x for x in outfiles if x.mute]) - if muted == outnum: - row.label(text='All output file are muted', icon='ERROR') - continue - - elif muted: - row.label(text=f'{muted}/{outnum} output file muted', icon='ERROR') - continue + scene_render_popup_ui(self, context) def execute(self, context): + d = fn.export_crop_to_json() + if not d: + print('No crop to export, border disabled in all scenes') + platform = sys.platform blend = Path(bpy.data.filepath) scn_to_render = [si.name for si in context.scene.scenes_list if si.select] - batch_file = blend.parent / f'{blend.stem}--{len(scn_to_render)}batch_{strftime("%m-%d-%H")}.sh' + batch_file = blend.parent / f'{blend.stem}--{len(scn_to_render)}batch_{strftime("%m-%d_%H-%M")}.sh' if platform.startswith('win'): script_text = ['@ECHO OFF'] @@ -177,27 +195,31 @@ class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator): script_text = ['#!/bin/bash'] print('batch_file: ', batch_file) + bin_path = bpy.app.binary_path for scn_name in scn_to_render: if platform.startswith('win'): import re pattern = r'users[\/\\](.*?)[\/\\]softs' # or point to user dit with %UserProfile% - re_user = re.search(pattern, bpy.app.binary_path) + re_user = re.search(pattern, bin_path, re.I) if not re_user: - cmd = f'"{bpy.app.binary_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a' + cmd = f'"{bin_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a' else: - bin_path = bpy.app.binary_path.replace(re_user.group(1), '%USERNAME%') + bin_path = bin_path.replace(re_user.group(1), '%USERNAME%') cmd = f'"{bin_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a' else: # Unix : point same for each user - cmd = f'"{bpy.app.binary_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a' + cmd = f'"{bin_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a' script_text.append(cmd) + script_text.append('echo --- END BATCH ---') script_text.append('pause') with batch_file.open('w') as fd: fd.write('\n'.join(script_text)) + print(f'Using following binary path: {bin_path}') + self.report({'INFO'}, f'Batch script generated: {batch_file}') return {"FINISHED"} diff --git a/__init__.py b/__init__.py index f36de0b..57b95dc 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, 6, 3), + "version": (0, 6, 4), "blender": (2, 93, 0), "location": "View3D", "warning": "", diff --git a/fn.py b/fn.py index c77d01f..55da25c 100644 --- a/fn.py +++ b/fn.py @@ -828,11 +828,12 @@ def export_crop_to_json(): for ob in [o for o in scn.objects if o.type == 'GPENCIL']: coord_dic[ob.name] = scn_border - # save bbox - with json_path.open('w') as fd: - json.dump(coord_dic, fd, indent='\t') + if coord_dic: + # save bbox + with json_path.open('w') as fd: + json.dump(coord_dic, fd, indent='\t') - print(f'coord saved at: {json_path}') + print(f'coord saved at: {json_path}') return coord_dic def set_border_region_from_coord(coords, scn=None, margin=30, export_json=True):