mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
176 lines
7.4 KiB
Python
176 lines
7.4 KiB
Python
import json
|
|
import re
|
|
|
|
from src.engines.core.base_engine import *
|
|
from src.utilities.misc_helper import system_safe_path
|
|
|
|
logger = logging.getLogger()
|
|
|
|
|
|
class Blender(BaseRenderEngine):
|
|
|
|
install_paths = ['/Applications/Blender.app/Contents/MacOS/Blender']
|
|
binary_names = {'linux': 'blender', 'windows': 'blender.exe', 'macos': 'Blender'}
|
|
file_extensions = ['blend']
|
|
|
|
@staticmethod
|
|
def downloader():
|
|
from src.engines.blender.blender_downloader import BlenderDownloader
|
|
return BlenderDownloader
|
|
|
|
@classmethod
|
|
def worker_class(cls):
|
|
from src.engines.blender.blender_worker import BlenderRenderWorker
|
|
return BlenderRenderWorker
|
|
|
|
def ui_options(self, project_info):
|
|
from src.engines.blender.blender_ui import BlenderUI
|
|
return BlenderUI.get_options(self)
|
|
|
|
def version(self):
|
|
version = None
|
|
try:
|
|
render_path = self.renderer_path()
|
|
if render_path:
|
|
ver_out = subprocess.check_output([render_path, '-v'], timeout=SUBPROCESS_TIMEOUT)
|
|
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
|
|
|
|
def get_output_formats(self):
|
|
format_string = self.get_help().split('Format Options')[-1].split('Animation Playback Options')[0]
|
|
formats = re.findall(r"'([A-Z_0-9]+)'", format_string)
|
|
return formats
|
|
|
|
def run_python_expression(self, project_path, python_expression, timeout=None):
|
|
if os.path.exists(project_path):
|
|
try:
|
|
return subprocess.run([self.renderer_path(), '-b', project_path, '--python-expr', python_expression],
|
|
capture_output=True, timeout=timeout)
|
|
except Exception as e:
|
|
logger.error(f"Error running python expression in blender: {e}")
|
|
else:
|
|
raise FileNotFoundError(f'Project file not found: {project_path}')
|
|
|
|
def run_python_script(self, script_path, project_path=None, timeout=None):
|
|
|
|
if project_path and 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}')
|
|
|
|
try:
|
|
command = [self.renderer_path(), '-b', '--python', script_path]
|
|
if project_path:
|
|
command.insert(2, project_path)
|
|
return subprocess.run(command, capture_output=True, timeout=timeout)
|
|
except Exception as e:
|
|
logger.exception(f"Error running python script in blender: {e}")
|
|
|
|
def get_project_info(self, project_path, timeout=10):
|
|
scene_info = {}
|
|
try:
|
|
script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'scripts', 'get_file_info.py')
|
|
results = self.run_python_script(project_path=project_path, script_path=system_safe_path(script_path),
|
|
timeout=timeout)
|
|
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
|
|
elif line.startswith('Error'):
|
|
logger.error(f"get_scene_info error: {line.strip()}")
|
|
except Exception as e:
|
|
logger.error(f'Error getting file details for .blend file: {e}')
|
|
return scene_info
|
|
|
|
def pack_project_file(self, project_path, timeout=30):
|
|
# Credit to L0Lock for pack script - https://blender.stackexchange.com/a/243935
|
|
try:
|
|
logger.info(f"Starting to pack Blender file: {project_path}")
|
|
script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'scripts', 'pack_project.py')
|
|
results = self.run_python_script(project_path=project_path, script_path=system_safe_path(script_path),
|
|
timeout=timeout)
|
|
|
|
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('Saved to: (.*)\n')
|
|
match = p.search(result_text)
|
|
if match:
|
|
new_path = os.path.join(dir_name, match.group(1).strip())
|
|
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
|
|
|
|
def get_arguments(self): # possibly deprecate
|
|
help_text = subprocess.check_output([self.renderer_path(), '-h']).decode('utf-8')
|
|
lines = help_text.splitlines()
|
|
|
|
options = {}
|
|
current_category = None
|
|
current_option = None
|
|
|
|
for line in lines:
|
|
line = line.strip()
|
|
# Check if line starts with - or --, indicating a new option
|
|
if line.endswith('Options:'):
|
|
current_category = line.split('Options')[0].strip()
|
|
options[current_category] = {}
|
|
elif line.startswith("-") or line.startswith("/"):
|
|
parts = line.split(' or ')
|
|
flag = parts[-1] # Choose the verbose option
|
|
has_argument = '<' in flag and '>' in flag
|
|
flag = flag.split(' <')[0] # Remove argument placeholder
|
|
current_option = flag.strip("--") or flag
|
|
options[current_category][current_option] = {
|
|
'flag': flag, 'description': '', 'takes_argument': has_argument
|
|
}
|
|
elif line == "" and current_option is not None:
|
|
current_option = None
|
|
elif current_option is not None:
|
|
d = options[current_category][current_option]['description']
|
|
d = d + (' ' if d else '') + line
|
|
options[current_category][current_option]['description'] = d
|
|
|
|
return options
|
|
|
|
def system_info(self):
|
|
return {'render_devices': self.get_render_devices()}
|
|
|
|
def get_render_devices(self):
|
|
script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'scripts', 'get_system_info.py')
|
|
results = self.run_python_script(script_path=script_path)
|
|
output = results.stdout.decode()
|
|
match = re.search(r"GPU DATA:(\[[\s\S]*\])", output)
|
|
if match:
|
|
gpu_data_json = match.group(1)
|
|
gpus_info = json.loads(gpu_data_json)
|
|
return gpus_info
|
|
else:
|
|
logger.error("GPU data not found in the output.")
|
|
|
|
def supported_render_engines(self):
|
|
engine_output = subprocess.run([self.renderer_path(), '-E', 'help'], timeout=SUBPROCESS_TIMEOUT,
|
|
capture_output=True).stdout.decode('utf-8').strip()
|
|
render_engines = [x.strip() for x in engine_output.split('Blender Engine Listing:')[-1].strip().splitlines()]
|
|
return render_engines
|
|
|
|
def perform_presubmission_tasks(self, project_path):
|
|
packed_path = self.pack_project_file(project_path, timeout=30)
|
|
return packed_path
|
|
|
|
|
|
if __name__ == "__main__":
|
|
x = Blender().get_render_devices()
|
|
print(x)
|