import bpy from . import fn from time import time, strftime from pathlib import Path import sys from bpy.types import Panel, UIList, Operator, PropertyGroup, Menu from bpy.props import PointerProperty, IntProperty, BoolProperty, StringProperty, EnumProperty, FloatProperty class GPEXP_OT_render_all_scenes(bpy.types.Operator): bl_idname = "gp.render_all_scenes" bl_label = "Render all scenes" bl_description = "Render all scene except Render" bl_options = {"REGISTER"} @classmethod def poll(cls, context): return True def execute(self, context): start = time() ct = 0 for scn in bpy.data.scenes: if scn.name == 'Scene': continue if not scn.use_nodes: continue outfiles = [n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE'] if not outfiles: print(f'\n -!-> Skip {scn.name}, No output files') continue if all(x.mute for x in outfiles): print(f'\n -!-> Skip {scn.name}, All output file are muted') continue print(f'\n --> Rendering {scn.name}') # bpy.context.window.scene = scn bpy.ops.render.render(animation=True, scene=scn.name) ct += 1 print(f'\nDone. {ct} scenes rendered in {time()-start:.2f}s') return {"FINISHED"} class GPEXP_scene_select_prop(PropertyGroup): name : StringProperty() select: BoolProperty() class GPEXP_OT_render_selected_scene(bpy.types.Operator): bl_idname = "gp.render_selected_scenes" bl_label = "Render Selected Scenes" bl_description = "Launch render of selected scenes with a selection popup" bl_options = {"REGISTER"} @classmethod def poll(cls, context): return True def invoke(self, context, event): context.scene.scenes_list.clear() for s in bpy.data.scenes: scn_item = context.scene.scenes_list.add() scn_item.name = s.name scn_item.select = s.name != 'Scene' 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='') def execute(self, context): 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: scn = bpy.data.scenes.get(scn_name) if not scn.use_nodes: print(f'{scn.name} has use node deactivated') continue outfiles = [n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE'] if not outfiles: print(f'\n -!-> Skip {scn.name}, No output files') continue if all(x.mute for x in outfiles): print(f'\n -!-> Skip {scn.name}, All output file are muted') continue print(f'\n --> Rendering {scn.name}') # bpy.context.window.scene = scn # no need bpy.ops.render.render(animation=True, scene=scn.name) ct += 1 print(f'\nDone. {ct} scenes rendered in {time()-start:.2f}s') return {"FINISHED"} class GPEXP_OT_bg_render_script_selected_scene(bpy.types.Operator): bl_idname = "gp.bg_render_script_selected_scenes" bl_label = "Create Selected Scene Render Batch " bl_description = "Create a batch script to render all selected scenes in a selection popup" bl_options = {"REGISTER"} @classmethod def poll(cls, context): return True def invoke(self, context, event): context.scene.scenes_list.clear() for s in bpy.data.scenes: scn_item = context.scene.scenes_list.add() scn_item.name = s.name scn_item.select = s.name != 'Scene' 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 def execute(self, context): 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' if platform.startswith('win'): script_text = ['@ECHO OFF'] batch_file = batch_file.with_suffix('.bat') else: script_text = ['#!/bin/bash'] print('batch_file: ', batch_file) 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) if not re_user: cmd = f'"{bpy.app.binary_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a' else: bin_path = bpy.app.binary_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' 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)) self.report({'INFO'}, f'Batch script generated: {batch_file}') return {"FINISHED"} classes=( GPEXP_scene_select_prop, GPEXP_OT_render_selected_scene, GPEXP_OT_render_all_scenes, GPEXP_OT_bg_render_script_selected_scene, ) def register(): for cls in classes: bpy.utils.register_class(cls) bpy.types.Scene.scenes_list = bpy.props.CollectionProperty(type=GPEXP_scene_select_prop) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) del bpy.types.Scene.scenes_list