Files
Zordon/lib/render_engines/blender_engine.py
2023-05-27 14:40:34 -05:00

104 lines
4.2 KiB
Python

try:
from .base_engine import *
except ImportError:
from base_engine import *
import json
import re
class Blender(BaseRenderEngine):
install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender']
supported_extensions = ['.blend']
@classmethod
def version(cls):
version = None
try:
render_path = cls.renderer_path()
if render_path:
ver_out = subprocess.check_output([render_path, '-v'])
version = ver_out.decode('utf-8').splitlines()[0].replace('Blender', '').strip()
except Exception as e:
logger.error(f'Failed to get Blender version: {e}')
return version
@classmethod
def get_output_formats(cls):
format_string = cls.get_help().split('Format Options')[-1].split('Animation Playback Options')[0]
formats = re.findall(r"'([A-Z_0-9]+)'", format_string)
return formats
@classmethod
def run_python_expression(cls, project_path, python_expression):
if os.path.exists(project_path):
try:
return subprocess.run([cls.renderer_path(), '-b', project_path, '--python-expr', python_expression],
capture_output=True)
except Exception as e:
logger.warning(f"Error running python expression in blender: {e}")
pass
else:
raise FileNotFoundError(f'Project file not found: {project_path}')
@classmethod
def run_python_script(cls, project_path, script_path):
if os.path.exists(project_path) and os.path.exists(script_path):
try:
return subprocess.run([cls.renderer_path(), '-b', project_path, '--python', script_path],
capture_output=True)
except Exception as e:
logger.warning(f"Error running python expression in blender: {e}")
pass
elif not os.path.exists(project_path):
raise FileNotFoundError(f'Project file not found: {project_path}')
elif not os.path.exists(script_path):
raise FileNotFoundError(f'Python script not found: {script_path}')
raise Exception("Uncaught exception")
@classmethod
def get_scene_info(cls, project_path):
scene_info = None
try:
results = cls.run_python_script(project_path, os.path.join(os.path.dirname(os.path.realpath(__file__)),
'scripts', 'get_blender_info.py'))
result_text = results.stdout.decode()
for line in result_text.splitlines():
if line.startswith('SCENE_DATA:'):
raw_data = line.split('SCENE_DATA:')[-1]
scene_info = json.loads(raw_data)
break
except Exception as e:
logger.error(f'Error getting file details for .blend file: {e}')
return scene_info
@classmethod
def pack_project_file(cls, project_path):
# Credit to L0Lock for pack script - https://blender.stackexchange.com/a/243935
pack_expression = "import bpy\n" \
"bpy.ops.file.pack_all()\n" \
"myPath = bpy.data.filepath\n" \
"myPath = str(myPath)\n" \
"bpy.ops.wm.save_as_mainfile(filepath=myPath[:-6]+'_packed'+myPath[-6:])"
try:
results = Blender.run_python_expression(project_path, pack_expression)
result_text = results.stdout.decode()
dir_name = os.path.dirname(project_path)
# report any missing textures
not_found = re.findall("(Unable to pack file, source path .*)\n", result_text)
for err in not_found:
logger.error(err)
p = re.compile('Info: Saved "(.*)"')
match = p.search(result_text)
if match:
new_path = os.path.join(dir_name, match.group(1))
logger.info(f'Blender file packed successfully to {new_path}')
return new_path
except Exception as e:
logger.error(f'Error packing .blend file: {e}')
return None