#!/usr/bin/env python3
import re
from collections import Counter
from src.engines.blender.blender_engine import Blender
from src.utilities.ffmpeg_helper import image_sequence_to_video
from src.engines.core.base_worker import *
class BlenderRenderWorker(BaseRenderWorker):
engine = Blender
def __init__(self, input_path, output_path, engine_path, args=None, parent=None, name=None):
super(BlenderRenderWorker, self).__init__(input_path=input_path, output_path=output_path, engine_path=engine_path, args=args, parent=parent, name=name)
# Stats
self.__frame_percent_complete = 0.0
# Scene Info
self.scene_info = {}
self.current_frame = -1
def generate_worker_subprocess(self):
self.scene_info = Blender(self.engine_path).get_project_info(self.input_path)
cmd = [self.engine_path]
if self.args.get('background', True): # optionally run render not in background
cmd.append('-b')
cmd.append(self.input_path)
# Set Render Engine
blender_engine = self.args.get('engine')
if blender_engine:
blender_engine = blender_engine.upper()
cmd.extend(['-E', blender_engine])
# Start Python expressions - # todo: investigate splitting into separate 'setup' script
cmd.append('--python-expr')
python_exp = 'import bpy; bpy.context.scene.render.use_overwrite = False;'
# Setup Custom Resolution
if self.args.get('resolution'):
res = self.args.get('resolution')
python_exp += 'bpy.context.scene.render.resolution_percentage = 100;'
python_exp += f'bpy.context.scene.render.resolution_x={res[0]}; bpy.context.scene.render.resolution_y={res[1]};'
# Setup Custom Camera
custom_camera = self.args.get('camera', None)
if custom_camera:
python_exp += f"bpy.context.scene.camera = bpy.data.objects['{custom_camera}'];"
# Set Render Device for Cycles (gpu/cpu/any)
if blender_engine == 'CYCLES':
render_device = self.args.get('render_device', 'any').lower()
if render_device not in {'any', 'gpu', 'cpu'}:
raise AttributeError(f"Invalid Cycles render device: {render_device}")
use_gpu = render_device in {'any', 'gpu'}
use_cpu = render_device in {'any', 'cpu'}
python_exp = python_exp + ("exec(\"for device in bpy.context.preferences.addons["
f"'cycles'].preferences.devices: device.use = {use_cpu} if device.type == 'CPU'"
f" else {use_gpu}\")")
# -- insert any other python exp checks / generators here --
# End Python expressions here
cmd.append(python_exp)
# Export format
export_format = self.args.get('export_format', None) or 'JPEG'
main_part, ext = os.path.splitext(self.output_path)
# Remove the extension only if it is not composed entirely of digits
path_without_ext = main_part if not ext[1:].isdigit() else self.output_path
path_without_ext += "_"
cmd.extend(['-o', path_without_ext, '-F', export_format])
# set frame range
cmd.extend(['-s', self.start_frame, '-e', self.end_frame, '-a'])
# Convert raw args from string if available
raw_args = self.get_raw_args()
if raw_args:
cmd.extend(raw_args)
return cmd
def _parse_stdout(self, line):
cycles_pattern = re.compile(
r'Fra:(?P\d*).*Mem:(?P\S+).*Time:(?P