psychopath/psychoblend/render.py
Nathan Vegdahl c372d485e2 Fix bug in PsychoBlend where render hangs.
This was because it was taking the standard input from Psychopath,
but was buffering it and not reading it.  The percentage complete
printouts would fill the buffer in some cases, and cause the
process to not exit since not all stdout had been handled.
2016-07-10 19:31:26 -07:00

154 lines
5.2 KiB
Python

import bpy
import time
import os
import subprocess
import tempfile
from . import psy_export
def get_temp_filename(suffix=""):
tmpf = tempfile.mkstemp(suffix=suffix, prefix='tmp')
os.close(tmpf[0])
return(tmpf[1])
class PsychopathRender(bpy.types.RenderEngine):
bl_idname = 'PSYCHOPATH_RENDER'
bl_label = "Psychopath"
DELAY = 1.0
@staticmethod
def _locate_binary():
addon_prefs = bpy.context.user_preferences.addons[__package__].preferences
# Use the system preference if its set.
psy_binary = addon_prefs.filepath_psychopath
if psy_binary:
if os.path.exists(psy_binary):
return psy_binary
else:
print("User Preference to psychopath %r NOT FOUND, checking $PATH" % psy_binary)
# search the path all os's
psy_binary_default = "psychopath"
os_path_ls = os.getenv("PATH").split(':') + [""]
for dir_name in os_path_ls:
psy_binary = os.path.join(dir_name, psy_binary_default)
if os.path.exists(psy_binary):
return psy_binary
return ""
def _export(self, scene, export_path, render_image_path):
exporter = psy_export.PsychoExporter(scene)
exporter.export_psy(export_path, render_image_path)
def _render(self, scene, psy_filepath):
psy_binary = PsychopathRender._locate_binary()
if not psy_binary:
print("Psychopath: could not execute psychopath, possibly Psychopath isn't installed")
return False
# TODO: figure out command line options
args = ["-i", psy_filepath]
# Start Rendering!
try:
self._process = subprocess.Popen([psy_binary] + args,
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
except OSError:
# TODO, report api
print("Psychopath: could not execute '%s'" % psy_binary)
import traceback
traceback.print_exc()
print ("***-DONE-***")
return False
return True
def _cleanup(self):
# for f in (self._temp_file_in, self._temp_file_ini, self._temp_file_out):
# for i in range(5):
# try:
# os.unlink(f)
# break
# except OSError:
# # Wait a bit before retrying file might be still in use by Blender,
# # and Windows does not know how to delete a file in use!
# time.sleep(self.DELAY)
# for i in unpacked_images:
# for c in range(5):
# try:
# os.unlink(i)
# break
# except OSError:
# # Wait a bit before retrying file might be still in use by Blender,
# # and Windows does not know how to delete a file in use!
# time.sleep(self.DELAY)
pass
def render(self, scene):
# has to be called to update the frame on exporting animations
scene.frame_set(scene.frame_current)
export_path = scene.psychopath.export_path
if export_path != "":
export_path += "_%d.psy" % scene.frame_current
else:
# Create a temporary file for exporting
export_path = get_temp_filename('.psy')
# Create a temporary file to render into
render_image_path = get_temp_filename('.png')
# start export
self.update_stats("", "Psychopath: Exporting data from Blender")
self._export(scene, export_path, render_image_path)
# Start rendering
self.update_stats("", "Psychopath: Rendering from exported file")
if not self._render(scene, export_path):
self.update_stats("", "Psychopath: Not found")
return
r = scene.render
# compute resolution
x = int(r.resolution_x * r.resolution_percentage)
y = int(r.resolution_y * r.resolution_percentage)
result = self.begin_result(0, 0, x, y)
lay = result.layers[0]
# TODO: Update viewport with render result while rendering
while self._process.poll() == None:
# Wait for self.DELAY seconds, but check for render cancels
# while waiting.
t = 0.0
while t < self.DELAY:
if self.test_break():
self._process.terminate()
break
time.sleep(0.05)
t += 0.05
# # Update viewport image with latest render output
# if os.path.exists(render_image_path):
# # This assumes the file has been fully written We wait a bit, just in case!
# try:
# lay.load_from_file(render_image_path)
# self.update_result(result)
# except RuntimeError:
# pass
# Load final image
lay.load_from_file(render_image_path)
self.end_result(result)
# Delete temporary image file
os.remove(render_image_path)
def register():
bpy.utils.register_class(PsychopathRender)
def unregister():
bpy.utils.unregister_class(PsychopathRender)