259 lines
8.9 KiB
Python
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'}
|