gp_render/OP_render_scenes.py

242 lines
8.0 KiB
Python

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()
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"
bl_description = "Launch render of selected scenes with a selection popup"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
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()
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):
## 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:
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 bpy.data.is_saved
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):
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-%M")}.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)
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, bin_path, re.I)
if not re_user:
cmd = f'"{bin_path}" -b "{bpy.data.filepath}" -S "{scn_name}" -a'
else:
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'"{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"}
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