MtsBlend: better integration
parent
afdbd4540a
commit
364d9bb13f
|
@ -31,29 +31,10 @@ bl_addon_info = {
|
|||
"category": "Render"}
|
||||
|
||||
|
||||
if "bpy" in locals():
|
||||
reload(render)
|
||||
reload(ui)
|
||||
|
||||
else:
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from render_mitsuba import render
|
||||
from render_mitsuba import ui
|
||||
from .core import RENDERENGINE_mitsuba
|
||||
|
||||
def register():
|
||||
Scene = bpy.types.Scene
|
||||
Scene.mts_path = bpy.props.StringProperty(
|
||||
name = "mts_path",
|
||||
description="Full path to the 'mitsuba' executable",
|
||||
default = "", subtype = "FILE_PATH", maxlen=1024)
|
||||
Scene.mts_gui = bpy.props.BoolProperty(
|
||||
name = "mts_gui",
|
||||
description="Should the Mitsuba user interface be launched?",
|
||||
default = False)
|
||||
RENDERENGINE_mitsuba.install()
|
||||
|
||||
def unregister():
|
||||
del Scene.mts_path
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
RENDERENGINE_mitsuba.uninstall()
|
|
@ -0,0 +1,145 @@
|
|||
# ##### 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 #####
|
||||
|
||||
# System libs
|
||||
import os, time, threading, subprocess, sys, copy
|
||||
|
||||
# Blender libs
|
||||
import bpy
|
||||
|
||||
# Framework libs
|
||||
from extensions_framework.engine import engine_base
|
||||
from extensions_framework import util as efutil
|
||||
|
||||
# Mitsuba-related classes
|
||||
from mitsuba.properties.engine import mitsuba_engine
|
||||
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
|
||||
|
||||
def compatible(mod):
|
||||
mod = __import__(mod)
|
||||
for subclass in mod.__dict__.values():
|
||||
try:
|
||||
subclass.COMPAT_ENGINES.add('mitsuba')
|
||||
except:
|
||||
pass
|
||||
del mod
|
||||
|
||||
|
||||
import properties_render
|
||||
properties_render.RENDER_PT_render.COMPAT_ENGINES.add('mitsuba')
|
||||
properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('mitsuba')
|
||||
properties_render.RENDER_PT_output.COMPAT_ENGINES.add('mitsuba')
|
||||
del properties_render
|
||||
|
||||
compatible("properties_data_mesh")
|
||||
compatible("properties_data_camera")
|
||||
|
||||
class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base):
|
||||
bl_idname = 'mitsuba'
|
||||
bl_label = 'Mitsuba'
|
||||
|
||||
property_groups = [
|
||||
('Scene', mitsuba_engine)
|
||||
]
|
||||
|
||||
render_lock = threading.Lock()
|
||||
|
||||
def process_wait_timer(self):
|
||||
# Nothing to do here
|
||||
pass
|
||||
|
||||
def render(self, scene):
|
||||
if scene is None:
|
||||
bpy.ops.ef.msg(msg_type='ERROR', msg_text='Scene to render is not valid')
|
||||
return
|
||||
|
||||
with self.render_lock: # just render one thing at a time
|
||||
scene_path = efutil.filesystem_path(scene.render.filepath)
|
||||
if os.path.isdir(scene_path):
|
||||
self.output_dir = scene_path
|
||||
else:
|
||||
self.output_dir = os.path.dirname(scene_path)
|
||||
if self.output_dir[-1] != '/':
|
||||
self.output_dir += '/'
|
||||
efutil.export_path = self.output_dir
|
||||
os.chdir(self.output_dir)
|
||||
|
||||
if scene.render.use_color_management == False:
|
||||
MtsLog('WARNING: Colour Management is switched off, render results may look too dark.')
|
||||
|
||||
MtsLog('MtsBlend: Current directory = "%s"' % self.output_dir)
|
||||
output_basename = efutil.scene_filename() + '.%s.%05i' % (scene.name, scene.frame_current)
|
||||
|
||||
export_result = bpy.ops.export.mitsuba(
|
||||
directory = self.output_dir,
|
||||
filename = output_basename,
|
||||
scene = scene.name
|
||||
)
|
||||
if 'CANCELLED' in export_result:
|
||||
return False
|
||||
|
||||
if scene.mitsuba_engine.export_mode == 'render':
|
||||
(mts_path, tail) = os.path.split(bpy.path.abspath(scene.mitsuba_engine.binary_path))
|
||||
mtsgui_binary = os.path.join(mts_path, "mtsgui")
|
||||
mitsuba_binary = os.path.join(mts_path, "mitsuba")
|
||||
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
|
||||
|
||||
MtsLog("MtsBlend: Launching renderer ..")
|
||||
if scene.mitsuba_engine.render_mode == 'gui':
|
||||
subprocess.Popen(
|
||||
[mtsgui_binary, efutil.export_path],
|
||||
env = env,
|
||||
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"
|
||||
|
||||
mitsuba_process = subprocess.Popen(
|
||||
[mitsuba_binary, efutil.export_path, '-o', output_image],
|
||||
env = env,
|
||||
cwd = self.output_dir
|
||||
)
|
||||
framebuffer_thread = MtsFilmDisplay({
|
||||
'resolution': resolution(scene),
|
||||
'RE': self,
|
||||
})
|
||||
framebuffer_thread.set_kick_period(scene.mitsuba_engine.refresh_interval)
|
||||
framebuffer_thread.start()
|
||||
while mitsuba_process.poll() == None and not self.test_break():
|
||||
self.render_update_timer = threading.Timer(1, self.process_wait_timer)
|
||||
self.render_update_timer.start()
|
||||
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':
|
||||
# Use SIGTERM because that's the only one supported on Windows
|
||||
luxrender_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)
|
|
@ -0,0 +1,56 @@
|
|||
class MtsAdjustments:
|
||||
def __init__(self, target_file):
|
||||
self.target_file = target_file
|
||||
|
||||
def export_worldtrafo(self, adjfile, trafo):
|
||||
adjfile.write('\t\t<transform name="toWorld">\n')
|
||||
adjfile.write('\t\t\t<matrix value="')
|
||||
for j in range(0,4):
|
||||
for i in range(0,4):
|
||||
adjfile.write("%f " % trafo[i][j])
|
||||
adjfile.write('"/>\n\t\t</transform>\n')
|
||||
|
||||
def export_lamp(self, adjfile, lamp, idx):
|
||||
if lamp.data.type == '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
|
||||
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</luminaire>\n')
|
||||
elif lamp.data.type == '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)
|
||||
|
||||
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)
|
||||
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</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))
|
||||
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))
|
||||
objFile.write('f 4 3 2 1\n')
|
||||
objFile.close()
|
||||
|
||||
def export(self, scene):
|
||||
adjfile = open(self.target_file, 'w')
|
||||
adjfile.write('<adjustments>\n');
|
||||
idx = 0
|
||||
for obj in scene.objects:
|
||||
if obj.type == 'LAMP':
|
||||
self.export_lamp(adjfile, obj, idx)
|
||||
idx = idx+1
|
||||
adjfile.write('</adjustments>\n');
|
||||
adjfile.close()
|
|
@ -0,0 +1,10 @@
|
|||
def resolution(scene):
|
||||
'''
|
||||
scene bpy.types.scene
|
||||
Calculate the output render resolution
|
||||
Returns tuple(2) (floats)
|
||||
'''
|
||||
xr = scene.render.resolution_x * scene.render.resolution_percentage / 100.0
|
||||
yr = scene.render.resolution_y * scene.render.resolution_percentage / 100.0
|
||||
|
||||
return xr, yr
|
|
@ -0,0 +1,172 @@
|
|||
# ##### 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 #####
|
||||
|
||||
# System Libs
|
||||
import os, copy, subprocess
|
||||
|
||||
# Blender Libs
|
||||
import bpy
|
||||
from presets import AddPresetBase
|
||||
|
||||
# Extensions_Framework Libs
|
||||
from extensions_framework import util as efutil
|
||||
|
||||
from mitsuba.outputs import MtsLog
|
||||
from mitsuba.export import MtsAdjustments
|
||||
|
||||
def try_preset_path_create(preset_subdir):
|
||||
target_path = os.path.join(bpy.utils.preset_paths('')[0], preset_subdir)
|
||||
if not os.path.exists(target_path):
|
||||
os.makedirs(target_path)
|
||||
|
||||
class MITSUBA_MT_base(object):
|
||||
preset_operator = "script.execute_preset"
|
||||
def draw(self, context):
|
||||
try_preset_path_create(self.preset_subdir)
|
||||
return bpy.types.Menu.draw_preset(self, context)
|
||||
|
||||
class MITSUBA_OT_preset_base(AddPresetBase):
|
||||
def execute(self, context):
|
||||
try_preset_path_create(self.preset_subdir)
|
||||
return super().execute(context)
|
||||
|
||||
class MITSUBA_MT_presets_engine(MITSUBA_MT_base, bpy.types.Menu):
|
||||
bl_label = "Mitsuba Engine Presets"
|
||||
preset_subdir = "mitsuba/engine"
|
||||
|
||||
class MITSUBA_OT_preset_engine_add(MITSUBA_OT_preset_base, bpy.types.Operator):
|
||||
'''Save the current settings as a preset'''
|
||||
bl_idname = 'mitsuba.preset_engine_add'
|
||||
bl_label = 'Add Mitsuba Engine settings preset'
|
||||
preset_menu = 'MITSUBA_MT_presets_engine'
|
||||
preset_values = [
|
||||
'bpy.context.scene.mitsuba_engine.%s'%v['attr'] for v in bpy.types.mitsuba_engine.get_exportable_properties()
|
||||
]
|
||||
preset_subdir = 'mitsuba/engine'
|
||||
|
||||
class MitsubaCheckOp(bpy.types.Operator):
|
||||
bl_idname = 'mts.check'
|
||||
bl_label = 'Check scene'
|
||||
|
||||
def reportWarning(self, msg):
|
||||
self.report({'WARNING'}, msg)
|
||||
print("MtsBlend: %s" % msg)
|
||||
|
||||
def _check_lamp(self, lamp):
|
||||
hasErrors = False
|
||||
if lamp.type == 'POINT' and lamp.falloff_type != 'INVERSE_SQUARE':
|
||||
self.reportWarning('Point light "%s" needs to have inverse square falloff' % lamp.name)
|
||||
hasErrors = True
|
||||
|
||||
if hasErrors:
|
||||
self.reportWarning('Encountered one or more problems -- check the console')
|
||||
else:
|
||||
self.report({'INFO'}, "No problems found")
|
||||
|
||||
def execute(self, context):
|
||||
scene = bpy.data.scenes[0]
|
||||
for obj in scene.objects:
|
||||
if obj.type == 'LAMP':
|
||||
self._check_lamp(obj.data)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class EXPORT_OT_mitsuba(bpy.types.Operator):
|
||||
bl_idname = 'export.mitsuba'
|
||||
bl_label = 'Export Mitsuba Scene (.xml)'
|
||||
|
||||
filename = bpy.props.StringProperty(name='Target filename')
|
||||
directory = bpy.props.StringProperty(name='Target directory')
|
||||
scene = bpy.props.StringProperty(options={'HIDDEN'}, default='')
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.add_fileselect(self)
|
||||
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
|
||||
|
||||
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!")
|
||||
return {'CANCELLED'}
|
||||
except OSError:
|
||||
self.report({'ERROR'}, "Could not execute '%s'" % mtsimport_binary)
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
menu_func = lambda self, context: self.layout.operator("export.mitsuba", text="Export Mitsuba scene...")
|
||||
bpy.types.INFO_MT_file_export.append(menu_func)
|
|
@ -0,0 +1,38 @@
|
|||
import os
|
||||
import bpy
|
||||
from extensions_framework import log
|
||||
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)
|
||||
'''
|
||||
if len(args) > 0:
|
||||
log(' '.join(['%s'%a for a in args]), module_name='Mitsuba', popup=popup)
|
||||
|
||||
class MtsFilmDisplay(TimerThread):
|
||||
'''
|
||||
Periodically update render result with Mituba's framebuffer
|
||||
'''
|
||||
|
||||
STARTUP_DELAY = 1
|
||||
|
||||
def kick(self, render_end=False):
|
||||
xres, yres = self.LocalStorage['resolution']
|
||||
|
||||
if render_end:
|
||||
MtsLog('Final render result %ix%i' % (xres,yres))
|
||||
else:
|
||||
MtsLog('Updating render result %ix%i' % (xres,yres))
|
||||
|
||||
result = self.LocalStorage['RE'].begin_result(0, 0, int(xres), int(yres))
|
||||
if os.path.exists(self.LocalStorage['RE'].output_file):
|
||||
bpy.ops.ef.msg(msg_text='Updating RenderResult')
|
||||
lay = result.layers[0]
|
||||
lay.load_from_file(self.LocalStorage['RE'].output_file)
|
||||
else:
|
||||
err_msg = 'ERROR: Could not load render result from %s' % self.LocalStorage['RE'].output_file
|
||||
MtsLog(err_msg)
|
||||
bpy.ops.ef.msg(msg_type='ERROR', msg_text=err_msg)
|
||||
self.LocalStorage['RE'].end_result(result)
|
|
@ -0,0 +1,85 @@
|
|||
# ##### 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 #####
|
||||
|
||||
from extensions_framework import declarative_property_group
|
||||
from extensions_framework import util as efutil
|
||||
|
||||
class mitsuba_engine(declarative_property_group):
|
||||
'''
|
||||
Storage class for Mitsuba Engine settings.
|
||||
This class will be instantiated within a Blender scene
|
||||
object.
|
||||
'''
|
||||
|
||||
controls = [
|
||||
'export_mode',
|
||||
'render_mode',
|
||||
'binary_path',
|
||||
'refresh_interval'
|
||||
]
|
||||
|
||||
visibility = {
|
||||
'render_mode': { 'export_mode': 'render' },
|
||||
'refresh_interval': { 'export_mode': 'render', 'render_mode' : 'cli' }
|
||||
}
|
||||
|
||||
properties = [
|
||||
{
|
||||
'type': 'enum',
|
||||
'attr': 'export_mode',
|
||||
'name': 'Export mode',
|
||||
'description': 'Specifies whether or not to launch the renderer after exporting the scene',
|
||||
'default': 'render',
|
||||
'items': [
|
||||
('render', 'Export + Render', 'render'),
|
||||
('exportonly', 'Only export', 'exportonly')
|
||||
],
|
||||
'save_in_preset': True
|
||||
},
|
||||
{
|
||||
'type': 'enum',
|
||||
'attr': 'render_mode',
|
||||
'name': 'Rendering mode',
|
||||
'description': 'Launch the external GUI or use the command-line renderer?',
|
||||
'default': 'cli',
|
||||
'items': [
|
||||
('cli', 'Mitsuba CLI', 'cli'),
|
||||
('gui', 'Mitsuba GUI', 'gui')
|
||||
],
|
||||
'save_in_preset': True
|
||||
},
|
||||
{
|
||||
'type': 'string',
|
||||
'subtype': 'DIR_PATH',
|
||||
'attr': 'binary_path',
|
||||
'name': 'Executable path',
|
||||
'description': 'Path to the "mitsuba" executable',
|
||||
'default': efutil.find_config_value('mitsuba', 'defaults', 'binary_path', '')
|
||||
},
|
||||
{
|
||||
'type': 'int',
|
||||
'attr': 'refresh_interval',
|
||||
'name': 'Refresh interval',
|
||||
'description': 'Period for updating rendering on screen (seconds)',
|
||||
'default': 10,
|
||||
'min': 1,
|
||||
'soft_min': 1,
|
||||
'save_in_preset': True
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
This directory contains a very basic plugin for Mitsuba <-> Blender
|
||||
integration. It is based on the excellent LuxBlend 2.5 code.
|
||||
|
||||
MtsBlend piggybacks on Blender's internal COLLADA exporter to get
|
||||
the scene information into Mitsuba. Whatever gets "lost in translation"
|
||||
is added back in using Mitsuba's adjustment mechanism.
|
|
@ -17,13 +17,6 @@
|
|||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import bpy
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
import copy
|
||||
import shutil
|
||||
import time
|
||||
|
||||
class MitsubaCheckOp(bpy.types.Operator):
|
||||
bl_idname = 'mts.check'
|
|
@ -0,0 +1,58 @@
|
|||
# ##### 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 bpy
|
||||
from properties_render import RenderButtonsPanel
|
||||
|
||||
from extensions_framework.ui import property_group_renderer
|
||||
|
||||
class render_described_context(RenderButtonsPanel, property_group_renderer):
|
||||
'''
|
||||
Base class for render engine settings panels
|
||||
'''
|
||||
|
||||
COMPAT_ENGINES = {'mitsuba'}
|
||||
|
||||
|
||||
class setup_preset(render_described_context, bpy.types.Panel):
|
||||
'''
|
||||
Engine settings presets UI Panel
|
||||
'''
|
||||
|
||||
bl_label = 'Mitsuba Engine Presets'
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
row.menu("MITSUBA_MT_presets_engine", text=bpy.types.MITSUBA_MT_presets_engine.bl_label)
|
||||
row.operator("mitsuba.preset_engine_add", text="", icon="ZOOMIN")
|
||||
row.operator("mitsuba.preset_engine_add", text="", icon="ZOOMOUT").remove_active = True
|
||||
|
||||
super().draw(context)
|
||||
|
||||
|
||||
class engine(render_described_context, bpy.types.Panel):
|
||||
'''
|
||||
Engine settings UI Panel
|
||||
'''
|
||||
|
||||
bl_label = 'Mitsuba Engine Configuration'
|
||||
|
||||
display_property_groups = [
|
||||
( ('scene',), 'mitsuba_engine' )
|
||||
]
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
# ##### 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 bpy
|
||||
|
||||
# Use some of the existing buttons.
|
||||
import properties_render
|
||||
properties_render.RENDER_PT_render.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
properties_render.RENDER_PT_antialiasing.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
properties_render.RENDER_PT_output.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
del properties_render
|
||||
|
||||
# Use only a subset of the world panels
|
||||
import properties_world
|
||||
properties_world.WORLD_PT_preview.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
properties_world.WORLD_PT_context_world.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
properties_world.WORLD_PT_world.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
properties_world.WORLD_PT_mist.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
del properties_world
|
||||
|
||||
# Example of wrapping every class 'as is'
|
||||
import properties_material
|
||||
for member in dir(properties_material):
|
||||
subclass = getattr(properties_material, member)
|
||||
try:
|
||||
subclass.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
except:
|
||||
pass
|
||||
del properties_material
|
||||
|
||||
import properties_data_mesh
|
||||
for member in dir(properties_data_mesh):
|
||||
subclass = getattr(properties_data_mesh, member)
|
||||
try:
|
||||
subclass.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
except:
|
||||
pass
|
||||
del properties_data_mesh
|
||||
|
||||
import properties_texture
|
||||
for member in dir(properties_texture):
|
||||
subclass = getattr(properties_texture, member)
|
||||
try:
|
||||
subclass.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
except:
|
||||
pass
|
||||
del properties_texture
|
||||
|
||||
import properties_data_camera
|
||||
for member in dir(properties_data_camera):
|
||||
subclass = getattr(properties_data_camera, member)
|
||||
try:
|
||||
subclass.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
except:
|
||||
pass
|
||||
del properties_data_camera
|
||||
|
||||
import properties_data_lamp
|
||||
for member in dir(properties_data_lamp):
|
||||
subclass = getattr(properties_data_lamp, member)
|
||||
try:
|
||||
subclass.COMPAT_ENGINES.add('MITSUBA_RENDER')
|
||||
except:
|
||||
pass
|
||||
del properties_data_lamp
|
||||
|
||||
class RenderButtonsPanel():
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "render"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
rd = context.scene.render
|
||||
return (rd.use_game_engine == False) and (rd.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class RENDER_PT_mitsuba_radiosity(RenderButtonsPanel, bpy.types.Panel):
|
||||
bl_label = "Mitsuba setup"
|
||||
COMPAT_ENGINES = {'MITSUBA_RENDER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scene = context.scene
|
||||
rd = scene.render
|
||||
|
||||
split = layout.split()
|
||||
col = split.column();
|
||||
col.prop(scene, "mts_path", text="Executable")
|
||||
row = col.row();
|
||||
row.prop(scene, "mts_gui", text="In external GUI")
|
||||
row.operator("wm.save_homefile", text="Save", icon ='FILE_TICK')
|
||||
col.operator("mts.check", text="Check scene")
|
Loading…
Reference in New Issue