diff --git a/engine/__init__.py b/engine/__init__.py index 5d41efda..76246d9d 100644 --- a/engine/__init__.py +++ b/engine/__init__.py @@ -55,6 +55,17 @@ def render(self, scene): config = self._session.GetRenderConfig() done = False + if scene.luxcore.config.use_filesaver: + self._session.Stop() + + output_path = config.GetProperties().Get("filesaver.directory").GetString() + self.report({"INFO"}, 'Exported to "%s"' % output_path) + + # Clean up + del self._session + self._session = None + return + # Fast refresh on startup so the user quickly sees an image forming. # Not used during animation render to enhance performance. if not self.is_animation: @@ -68,7 +79,7 @@ def render(self, scene): if now - last_refresh > refresh_interval: stats = utils_render.refresh(self, scene, config, draw_film=True) - done = utils_render.halt_condition_met(scene, stats) or self.test_break() + done = utils_render.halt_condition_met(scene, stats) or self.test_break() or self._session.HasDone() if now - start > FAST_REFRESH_DURATION: # It's time to switch to the loop with slow refresh below @@ -97,13 +108,13 @@ def render(self, scene): draw_film = time_until_film_refresh <= 0 stats = utils_render.refresh(self, scene, config, draw_film, time_until_film_refresh) - done = utils_render.halt_condition_met(scene, stats) or self.test_break() + done = utils_render.halt_condition_met(scene, stats) or self.test_break() or self._session.HasDone() last_stat_refresh = now if draw_film: last_film_refresh = now - # Don't use up too much CPU time + # Don't use up too much CPU time for this refresh loop, but stay responsive sleep(1 / 60) # User wants to stop or halt condition is reached diff --git a/export/config.py b/export/config.py index 62996e3c..5d5b6a05 100644 --- a/export/config.py +++ b/export/config.py @@ -1,3 +1,6 @@ +import os +import errno +import bpy from ..bin import pyluxcore from .. import utils @@ -73,19 +76,41 @@ def convert(scene, context=None): # if we forget some in the if/else construct above. definitions.update({ "renderengine.type": engine, - # "renderengine.type": "FILESAVER", - # "filesaver.directory": "./testscene/", - # "filesaver.renderengine.type": engine, "sampler.type": sampler, "film.width": width, "film.height": height, "film.filter.type": "BLACKMANHARRIS" if config.use_filter else "NONE", "film.filter.width": config.filter_width, - # ub.enabled = context.scene.render.threads_mode == 'FIXED' - # sub.prop(context.scene.render, "threads") - }) + # FILESAVER engine (only in final render) + use_filesaver = context is None and config.use_filesaver + if use_filesaver: + output_path = utils.get_abspath(scene.render.filepath, must_exist=True) + + if output_path is None: + raise OSError('Not a valid output path: "%s"' % scene.render.filepath) + + blend_name = bpy.path.basename(bpy.context.blend_data.filepath) + blend_name = os.path.splitext(blend_name)[0] # remove ".blend" + if not blend_name: + blend_name = "Untitled" + dir_name = blend_name + "_LuxCore" + frame_name = "%05d" % scene.frame_current + output_path = os.path.join(output_path, dir_name, frame_name) + + if not os.path.exists(output_path): + # https://stackoverflow.com/a/273227 + try: + os.makedirs(output_path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + definitions["renderengine.type"] = "FILESAVER" + definitions["filesaver.renderengine.type"] = engine + definitions["filesaver.directory"] = output_path + # CPU thread settings (we use the properties from Blender here) if scene.render.threads_mode == "FIXED": definitions["native.threads.count"] = scene.render.threads @@ -97,6 +122,10 @@ def convert(scene, context=None): definitions["film.imagepipeline.0.type"] = "TONEMAP_AUTOLINEAR" definitions["film.imagepipeline.1.type"] = "TONEMAP_LINEAR" definitions["film.imagepipeline.1.scale"] = 1 / 2.25 + if use_filesaver: + # Needs gamma correction + definitions["film.imagepipeline.2.type"] = "GAMMA_CORRECTION" + definitions["film.imagepipeline.2.value"] = 2.2 return utils.create_props(prefix, definitions) except Exception as error: diff --git a/export/light.py b/export/light.py index 95bfd8a4..69beaf3f 100644 --- a/export/light.py +++ b/export/light.py @@ -172,7 +172,6 @@ def convert_lamp(blender_obj, scene, context, luxcore_scene): raise Exception("Unkown light type", lamp.type, 'in lamp "%s"' % blender_obj.name) props = utils.create_props(prefix, definitions) - print(props) return props, exported_light except Exception as error: msg = 'Light "%s": %s' % (blender_obj.name, error) diff --git a/nodes/volumes/__init__.py b/nodes/volumes/__init__.py index 4df70a88..9acd82de 100644 --- a/nodes/volumes/__init__.py +++ b/nodes/volumes/__init__.py @@ -3,6 +3,7 @@ import nodeitems_utils from nodeitems_utils import NodeCategory, NodeItem, NodeItemCustom from ...ui import ICON_VOLUME +from ..output import get_active_output from .output import LuxCoreNodeVolOutput from .clear import LuxCoreNodeVolClear @@ -14,6 +15,8 @@ class LuxCoreVolumeNodeTree(NodeTree): bl_label = "LuxCore Volume Nodes" bl_icon = ICON_VOLUME + # last_node_tree = None + @classmethod def poll(cls, context): return context.scene.render.engine == "LUXCORE" @@ -28,13 +31,20 @@ def poll(cls, context): # mat = obj.active_material # # if mat: - # # ID pointer - # node_tree = mat.luxcore.node_tree + # mat_node_tree = mat.luxcore.node_tree + # if mat_node_tree is None: + # return cls.last_node_tree, mat, mat + # + # output = get_active_output(mat_node_tree) + # interior = output.interior_volume + # exterior = output.exterior_volume # - # if node_tree: - # return node_tree, mat, mat + # if interior: + # cls.last_node_tree = interior + # if exterior: + # cls.last_node_tree = exterior # - # return None, None, None + # return cls.last_node_tree, None, None # This block updates the preview, when socket links change def update(self): diff --git a/properties/config.py b/properties/config.py index 40bff742..af3a4e11 100644 --- a/properties/config.py +++ b/properties/config.py @@ -141,3 +141,5 @@ class LuxCoreConfig(PropertyGroup): # Pixel filter use_filter = BoolProperty(name="Use Pixel Filter", default=True, description=FILTER_DESC) filter_width = FloatProperty(name="Width", default=1.5, min=0.5, soft_max=3, description=FILTER_WIDTH_DESC) + + use_filesaver = BoolProperty(name="Only write LuxCore scene", default=False) diff --git a/ui/config.py b/ui/config.py index b783340a..6197c866 100644 --- a/ui/config.py +++ b/ui/config.py @@ -16,6 +16,13 @@ def draw(self, context): layout = self.layout config = context.scene.luxcore.config + # Filesaver + # TODO: we might want to move this to a more appropriate place later + layout.prop(config, "use_filesaver") + if config.use_filesaver: + layout.prop(context.scene.render, "filepath") + layout.separator() + # Device row_device = layout.row() row_device.enabled = config.engine == "PATH"