mitsuba/tools/blender/mitsuba/operators/__init__.py

259 lines
8.9 KiB
Python

# ##### 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, sys, copy, subprocess, traceback, string
# 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.adjustments 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 MITSUBA_MT_presets_texture(MITSUBA_MT_base, bpy.types.Menu):
bl_label = "Mitsuba Texture Presets"
preset_subdir = "mitsuba/texture"
class MITSUBA_OT_preset_texture_add(MITSUBA_OT_preset_base, bpy.types.Operator):
'''Save the current settings as a preset'''
bl_idname = 'mitsuba.preset_texture_add'
bl_label = 'Add Mitsuba Texture settings preset'
preset_menu = 'MITSUBA_MT_presets_texture'
preset_values = []
preset_subdir = 'mitsuba/texture'
def execute(self, context):
pv = [
'bpy.context.texture.mitsuba_texture.%s'%v['attr'] for v in bpy.types.mitsuba_texture.get_exportable_properties()
]
mts_type = context.texture.mitsuba_texture.type
sub_type = getattr(bpy.types, 'mitsuba_tex_%s' % mts_type)
pv.extend([
'bpy.context.texture.mitsuba_texture.mitsuba_tex_%s.%s'%(mts_type, v['attr']) for v in sub_type.get_exportable_properties()
])
pv.extend([
'bpy.context.texture.mitsuba_texture.mitsuba_tex_mapping.%s'%v['attr'] for v in bpy.types.mitsuba_tex_mapping.get_exportable_properties()
])
self.preset_values = pv
return super().execute(context)
class MITSUBA_MT_presets_material(MITSUBA_MT_base, bpy.types.Menu):
bl_label = "Mitsuba Material Presets"
preset_subdir = "mitsuba/material"
class MITSUBA_OT_preset_material_add(MITSUBA_OT_preset_base, bpy.types.Operator):
'''Save the current settings as a preset'''
bl_idname = 'mitsuba.preset_material_add'
bl_label = 'Add Mitsuba Material settings preset'
preset_menu = 'MITSUBA_MT_presets_material'
preset_values = []
preset_subdir = 'mitsuba/material'
def execute(self, context):
pv = [
'bpy.context.material.mitsuba_material.%s'%v['attr'] for v in bpy.types.mitsuba_material.get_exportable_properties()
]
# store only the sub-properties of the selected mitsuba material type
mts_type = context.material.mitsuba_material.type
sub_type = getattr(bpy.types, 'mitsuba_mat_%s' % mts_type)
pv.extend([
'bpy.context.material.mitsuba_material.mitsuba_mat_%s.%s'%(mts_type, v['attr']) for v in sub_type.get_exportable_properties()
])
pv.extend([
'bpy.context.material.mitsuba_material.mitsuba_emission.%s'%v['attr'] for v in bpy.types.mitsuba_emission.get_exportable_properties()
])
self.preset_values = pv
return super().execute(context)
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):
try:
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)
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'}
(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:
command = [mtsimport_binary, '-r', '%dx%d' % (width, height),
'-n', '-l', 'pngfilm', mts_dae_file, mts_xml_file, mts_adj_file]
if scene.mitsuba_integrator.motionblur:
command += ['-z']
process = subprocess.Popen(command,
env = env,
cwd = mts_path
)
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)
class MITSUBA_OT_material_slot_move(bpy.types.Operator):
''' Rearrange the material slots '''
bl_idname = 'mitsuba.material_slot_move'
bl_label = 'Move a material entry up or down'
type = bpy.props.StringProperty(name='type')
def execute(self, context):
obj = bpy.context.active_object
index = obj.active_material_index
new_index = index-1 if self.properties.type == 'UP' else index+1
size = len(obj.material_slots)
if new_index >= 0 and new_index < size:
obj.active_material_index = 0
# Can't write to material_slots, hence the kludge
materials = []
for i in range(0, size):
materials += [obj.material_slots[i].name]
for i in range(0, size):
mat = obj.data.materials.pop(0)
del(mat)
temp = materials[index]
materials[index] = materials[new_index]
materials[new_index] = temp
for i in range(0, size):
obj.data.materials.append(bpy.data.materials[materials[i]])
obj.active_material_index = new_index
return {'FINISHED'}
class MITSUBA_OT_material_add(bpy.types.Operator):
''' Append a new material '''
bl_idname = 'mitsuba.material_add'
bl_label = 'Append a new material'
type = bpy.props.StringProperty(name='type')
def execute(self, context):
obj = bpy.context.active_object
index = obj.active_material_index
if len(obj.material_slots) == 0:
curName = 'Material'
else:
curName = obj.material_slots[index].name
mat = bpy.data.materials.new(name=curName)
obj.data.materials.append(mat)
obj.active_material_index = len(obj.data.materials)-1
return {'FINISHED'}