MtsBlend: support all light source types
parent
2a53d3240f
commit
846b314676
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue