MtsBlend: support all light source types

metadata
Wenzel Jakob 2010-11-12 00:56:19 +01:00
parent 2a53d3240f
commit 846b314676
5 changed files with 165 additions and 82 deletions

View File

@ -331,8 +331,10 @@ int ubi_main(int argc, char **argv) {
testSupervisor->printSummary();
} catch (const std::exception &e) {
std::cerr << "Caught a critical exeption: " << e.what() << std::endl;
return -1;
} catch (...) {
std::cerr << "Caught a critical exeption of unknown type!" << endl;
return -1;
}
return 0;

View File

@ -28,11 +28,13 @@ from extensions_framework import util as efutil
# Mitsuba-related classes
from mitsuba.properties.engine import mitsuba_engine
from mitsuba.properties.lamp import mitsuba_lamp
from mitsuba.operators import MITSUBA_OT_preset_engine_add, EXPORT_OT_mitsuba
from mitsuba.outputs import MtsLog, MtsFilmDisplay
from mitsuba.export.film import resolution
from mitsuba.ui import render_panels
from mitsuba.ui import lamps
def compatible(mod):
mod = __import__(mod)
@ -44,6 +46,10 @@ def compatible(mod):
del mod
import properties_data_lamp
properties_data_lamp.DATA_PT_context_lamp.COMPAT_ENGINES.add('mitsuba')
del properties_data_lamp
import properties_render
properties_render.RENDER_PT_render.COMPAT_ENGINES.add('mitsuba')
properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('mitsuba')
@ -58,7 +64,8 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base):
bl_label = 'Mitsuba'
property_groups = [
('Scene', mitsuba_engine)
('Scene', mitsuba_engine),
('Lamp', mitsuba_lamp)
]
render_lock = threading.Lock()
@ -95,7 +102,8 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base):
scene = scene.name
)
if 'CANCELLED' in export_result:
return False
bpy.ops.ef.msg(msg_type='ERROR', msg_text='Error while exporting -- check the console for details.')
return
if scene.mitsuba_engine.export_mode == 'render':
(mts_path, tail) = os.path.split(bpy.path.abspath(scene.mitsuba_engine.binary_path))
@ -115,11 +123,10 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base):
cwd = self.output_dir
)
elif scene.mitsuba_engine.render_mode == 'cli':
(output_image, _) = os.path.splitext(efutil.export_path)
self.output_file = output_image + ".png"
self.output_file = efutil.export_path[:-4] + ".png"
mitsuba_process = subprocess.Popen(
[mitsuba_binary, efutil.export_path, '-o', output_image],
[mitsuba_binary, efutil.export_path, '-o', self.output_file],
env = env,
cwd = self.output_dir
)
@ -135,11 +142,15 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base):
if self.render_update_timer.isAlive(): self.render_update_timer.join()
# If we exit the wait loop (user cancelled) and luxconsole is still running, then send SIGINT
if mitsuba_process.poll() == None and scene.luxrender_engine.binary_name != 'luxrender':
if mitsuba_process.poll() == None:
# Use SIGTERM because that's the only one supported on Windows
luxrender_process.send_signal(subprocess.signal.SIGTERM)
mitsuba_process.send_signal(subprocess.signal.SIGTERM)
# Stop updating the render result and load the final image
framebuffer_thread.stop()
framebuffer_thread.join()
framebuffer_thread.kick(render_end=True)
if mitsuba_process.poll() != None and mitsuba_process.returncode != 0:
bpy.ops.ef.msg(msg_type='ERROR', msg_text='Rendering failed -- check the console.')
else:
framebuffer_thread.kick(render_end=True)

View File

@ -1,6 +1,35 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import os, math, mathutils
from extensions_framework import util as efutil
class MtsAdjustments:
def __init__(self, target_file):
'''
Writes scene information to an "adjustments" file. This
mechanism is used to capture information which gets lost
in translation when using the COLLADA exporter.
'''
def __init__(self, target_file, target_dir):
self.target_file = target_file
self.target_dir = target_dir
def export_worldtrafo(self, adjfile, trafo):
adjfile.write('\t\t<transform name="toWorld">\n')
@ -11,31 +40,33 @@ class MtsAdjustments:
adjfile.write('"/>\n\t\t</transform>\n')
def export_lamp(self, adjfile, lamp, idx):
if lamp.data.type == 'POINT':
ltype = lamp.data.mitsuba_lamp.type
if ltype == 'POINT':
adjfile.write('\t<luminaire id="%s-light" type="point">\n' % lamp.data.name)
mult = lamp.data.energy * lamp.data.distance * lamp.data.distance / 2
mult = lamp.data.mitsuba_lamp.intensity
self.export_worldtrafo(adjfile, lamp.matrix_world)
adjfile.write('\t\t<rgb name="intensity" value="%f %f %f"/>\n'
% (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult))
adjfile.write('\t\t<float name="samplingWeight" value="%f"/>\n' % lamp.data.mitsuba_lamp.sampling_weight)
adjfile.write('\t</luminaire>\n')
elif lamp.data.type == 'AREA':
elif ltype == 'AREA':
adjfile.write('\t<shape type="obj">\n')
size_x = lamp.data.size
size_y = lamp.data.size
if lamp.data.shape == 'RECTANGLE':
size_y = lamp.data.size_y
path = os.path.join(os.path.join(self._temp_dir, 'meshes'), "_area_luminaire_%d.obj" % idx)
path = os.path.join(os.path.join(self.target_dir, 'meshes'), "_area_luminaire_%d.obj" % idx)
adjfile.write('\t\t<string name="filename" value="%s"/>\n' % path)
self.export_worldtrafo(adjfile, lamp.matrix_world)
adjfile.write('\n\t\t<luminaire id="%s-light" type="area">\n' % lamp.data.name)
mult = lamp.data.energy * lamp.data.distance * lamp.data.distance / (size_x * size_y)
mult = lamp.data.mitsuba_lamp.intensity / (2 * size_x * size_y)
adjfile.write('\t\t\t<rgb name="intensity" value="%f %f %f"/>\n'
% (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult))
adjfile.write('\t\t\t<float name="samplingWeight" value="%f"/>\n' % lamp.data.mitsuba_lamp.sampling_weight)
adjfile.write('\t\t</luminaire>\n')
adjfile.write('\t</shape>\n')
objFile = open(path, 'w')
objFile.write('v %f %f 0\n' % (-size_x/2, -size_y/2))
objFile.write('v %f %f 0\n' % ( size_x/2, -size_y/2))
@ -43,6 +74,39 @@ class MtsAdjustments:
objFile.write('v %f %f 0\n' % (-size_x/2, size_y/2))
objFile.write('f 4 3 2 1\n')
objFile.close()
elif ltype == 'SUN':
adjfile.write('\t<luminaire id="%s-light" type="directional">\n' % lamp.data.name)
mult = lamp.data.mitsuba_lamp.intensity
scale = mathutils.Matrix.Scale(-1, 4, mathutils.Vector([0, 0, 1]))
self.export_worldtrafo(adjfile, lamp.matrix_world * mathutils.Matrix.Scale(-1, 4, mathutils.Vector([0, 0, 1])))
adjfile.write('\t\t<rgb name="intensity" value="%f %f %f"/>\n'
% (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult))
adjfile.write('\t\t<float name="samplingWeight" value="%f"/>\n' % lamp.data.mitsuba_lamp.sampling_weight)
adjfile.write('\t</luminaire>\n')
elif ltype == 'SPOT':
adjfile.write('\t<luminaire id="%s-light" type="spot">\n' % lamp.data.name)
mult = lamp.data.mitsuba_lamp.intensity
self.export_worldtrafo(adjfile, lamp.matrix_world * mathutils.Matrix.Scale(-1, 4, mathutils.Vector([0, 0, 1])))
adjfile.write('\t\t<rgb name="intensity" value="%f %f %f"/>\n'
% (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult))
adjfile.write('\t\t<float name="cutoffAngle" value="%f"/>\n' % (lamp.data.spot_size * 180 / (math.pi * 2)))
adjfile.write('\t\t<float name="beamWidth" value="%f"/>\n' % (lamp.data.spot_blend * lamp.data.spot_size * 180 / (math.pi * 2)))
adjfile.write('\t\t<float name="samplingWeight" value="%f"/>\n' % lamp.data.mitsuba_lamp.sampling_weight)
adjfile.write('\t</luminaire>\n')
elif ltype == 'ENV':
if lamp.data.mitsuba_lamp.envmap_type == 'constant':
adjfile.write('\t<luminaire id="%s-light" type="constant">\n' % lamp.data.name)
mult = lamp.data.mitsuba_lamp.intensity
adjfile.write('\t\t<rgb name="intensity" value="%f %f %f"/>\n'
% (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult))
adjfile.write('\t\t<float name="samplingWeight" value="%f"/>\n' % lamp.data.mitsuba_lamp.sampling_weight)
adjfile.write('\t</luminaire>\n')
elif lamp.data.mitsuba_lamp.envmap_type == 'envmap':
adjfile.write('\t<luminaire id="%s-light" type="envmap">\n' % lamp.data.name)
adjfile.write('\t\t<string name="filename" value="%s"/>\n' % efutil.filesystem_path(lamp.data.mitsuba_lamp.envmap_file))
self.export_worldtrafo(adjfile, lamp.matrix_world)
adjfile.write('\t\t<float name="intensityScale" value="%f"/>\n' % lamp.data.mitsuba_lamp.intensity)
adjfile.write('\t</luminaire>\n')
def export(self, scene):
adjfile = open(self.target_file, 'w')

View File

@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
# System Libs
import os, copy, subprocess
import os, sys, copy, subprocess, traceback, string
# Blender Libs
import bpy
@ -65,7 +65,7 @@ class MitsubaCheckOp(bpy.types.Operator):
def reportWarning(self, msg):
self.report({'WARNING'}, msg)
print("MtsBlend: %s" % msg)
MtsLog("MtsBlend: %s" % msg)
def _check_lamp(self, lamp):
hasErrors = False
@ -99,74 +99,80 @@ class EXPORT_OT_mitsuba(bpy.types.Operator):
return {'RUNNING_MODAL'}
def execute(self, context):
if self.properties.scene == '':
scene = context.scene
else:
scene = bpy.data.scenes[self.properties.scene]
if scene is None:
self.report({'ERROR'}, 'Scene is not valid for export to %s' % self.properties.filename)
return {'CANCELLED'}
# Force scene update; NB, scene.update() doesn't work
scene.frame_set(scene.frame_current)
(self.properties.filename, _) = os.path.splitext(self.properties.filename)
mts_basename = os.path.join(
self.properties.directory,
self.properties.filename)
mts_dae_file = mts_basename + ".dae"
mts_xml_file = mts_basename + ".xml"
mts_adj_file = mts_basename + "_adjustments.xml"
mts_meshes_dir = os.path.join(self.properties.directory, "meshes")
efutil.export_path = mts_xml_file
try:
os.mkdir(mts_meshes_dir)
except OSError:
pass
if self.properties.scene == '':
scene = context.scene
else:
scene = bpy.data.scenes[self.properties.scene]
scene.collada_export(mts_dae_file)
MtsLog('MtsBlend: Writing adjustments file to "%s"' % mts_adj_file)
adj = MtsAdjustments(mts_adj_file)
adj.export(scene)
if scene.mitsuba_engine.binary_path == "":
self.report({'ERROR'}, 'Mitsuba binary path must be specified!')
return {'CANCELLED'}
scene.mitsuba_engine.binary_path = efutil.filesystem_path(scene.mitsuba_engine.binary_path)
efutil.write_config_value('mitsuba', 'defaults', 'binary_path', scene.mitsuba_engine.binary_path)
(mts_path, tail) = os.path.split(bpy.path.abspath(scene.mitsuba_engine.binary_path))
mtsimport_binary = os.path.join(mts_path, "mtsimport")
env = copy.copy(os.environ)
mts_render_libpath = os.path.join(mts_path, "src/librender")
mts_core_libpath = os.path.join(mts_path, "src/libcore")
mts_hw_libpath = os.path.join(mts_path, "src/libhw")
env['LD_LIBRARY_PATH'] = mts_core_libpath + ":" + mts_render_libpath + ":" + mts_hw_libpath
render = scene.render
width = int(render.resolution_x * render.resolution_percentage * 0.01)
height = int(render.resolution_y * render.resolution_percentage * 0.01)
MtsLog("MtsBlend: Launching mtsimport")
try:
process = subprocess.Popen(
[mtsimport_binary, '-r', '%dx%d' % (width, height),
'-l', 'pngfilm', mts_dae_file, mts_xml_file, mts_adj_file],
env = env,
cwd = self.properties.directory
)
if process.wait() != 0:
self.report({'ERROR'}, "mtsimport returned with a nonzero status!")
if scene is None:
self.report({'ERROR'}, 'Scene is not valid for export to %s' % self.properties.filename)
return {'CANCELLED'}
except OSError:
self.report({'ERROR'}, "Could not execute '%s'" % mtsimport_binary)
return {'CANCELLED'}
# Force scene update; NB, scene.update() doesn't work
scene.frame_set(scene.frame_current)
return {'FINISHED'}
(self.properties.filename, _) = os.path.splitext(self.properties.filename)
mts_basename = os.path.join(
self.properties.directory,
self.properties.filename)
mts_dae_file = mts_basename + ".dae"
mts_xml_file = mts_basename + ".xml"
mts_adj_file = mts_basename + "_adjustments.xml"
mts_meshes_dir = os.path.join(self.properties.directory, "meshes")
efutil.export_path = mts_xml_file
try:
os.mkdir(mts_meshes_dir)
except OSError:
pass
scene.collada_export(mts_dae_file)
MtsLog('MtsBlend: Writing adjustments file to "%s"' % mts_adj_file)
adj = MtsAdjustments(mts_adj_file, self.properties.directory)
adj.export(scene)
if scene.mitsuba_engine.binary_path == "":
self.report({'ERROR'}, 'Mitsuba binary path must be specified!')
return {'CANCELLED'}
scene.mitsuba_engine.binary_path = efutil.filesystem_path(scene.mitsuba_engine.binary_path)
efutil.write_config_value('mitsuba', 'defaults', 'binary_path', scene.mitsuba_engine.binary_path)
(mts_path, tail) = os.path.split(bpy.path.abspath(scene.mitsuba_engine.binary_path))
mtsimport_binary = os.path.join(mts_path, "mtsimport")
env = copy.copy(os.environ)
mts_render_libpath = os.path.join(mts_path, "src/librender")
mts_core_libpath = os.path.join(mts_path, "src/libcore")
mts_hw_libpath = os.path.join(mts_path, "src/libhw")
env['LD_LIBRARY_PATH'] = mts_core_libpath + ":" + mts_render_libpath + ":" + mts_hw_libpath
render = scene.render
width = int(render.resolution_x * render.resolution_percentage * 0.01)
height = int(render.resolution_y * render.resolution_percentage * 0.01)
MtsLog("MtsBlend: Launching mtsimport")
try:
process = subprocess.Popen(
[mtsimport_binary, '-r', '%dx%d' % (width, height),
'-l', 'pngfilm', mts_dae_file, mts_xml_file, mts_adj_file],
env = env,
cwd = self.properties.directory
)
if process.wait() != 0:
self.report({'ERROR'}, "mtsimport returned with a nonzero status!")
return {'CANCELLED'}
except OSError:
self.report({'ERROR'}, "Could not execute '%s'" % mtsimport_binary)
return {'CANCELLED'}
return {'FINISHED'}
except:
typ, value, tb = sys.exc_info()
elist = traceback.format_exception(typ, value, tb)
MtsLog("Caught exception: %s" % ''.join(elist))
return {'CANCELLED'}
menu_func = lambda self, context: self.layout.operator("export.mitsuba", text="Export Mitsuba scene...")
bpy.types.INFO_MT_file_export.append(menu_func)

View File

@ -6,7 +6,7 @@ from extensions_framework.util import TimerThread
def MtsLog(*args, popup=False):
'''
Send string to AF log, marked as belonging to Mitsuba module.
Accepts variable args (can be used as pylux.errorHandler)
Accepts variable args
'''
if len(args) > 0:
log(' '.join(['%s'%a for a in args]), module_name='Mitsuba', popup=popup)