From fd68671dbc0e0f5238b6fa601fdee47ddcfccf64 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 13 Nov 2010 01:40:31 +0100 Subject: [PATCH] MtsBlend: support for textures and materials --- src/textures/checkerboard.cpp | 52 +-- src/textures/gridtexture.cpp | 52 +-- tools/blender/mitsuba/core/__init__.py | 22 +- tools/blender/mitsuba/export/__init__.py | 171 ++++---- tools/blender/mitsuba/export/adjustments.py | 172 ++++++++ tools/blender/mitsuba/operators/__init__.py | 75 ++-- tools/blender/mitsuba/properties/material.py | 66 ++++ tools/blender/mitsuba/properties/texture.py | 368 ++++++++++++++++++ tools/blender/mitsuba/ui/lamps.py | 3 +- .../blender/mitsuba/ui/materials/__init__.py | 37 ++ .../mitsuba/ui/materials/lambertian.py | 30 ++ tools/blender/mitsuba/ui/materials/main.py | 40 ++ tools/blender/mitsuba/ui/textures/__init__.py | 41 ++ .../mitsuba/ui/textures/checkerboard.py | 30 ++ .../blender/mitsuba/ui/textures/ldrtexture.py | 30 ++ tools/blender/mitsuba/ui/textures/main.py | 48 +++ 16 files changed, 1061 insertions(+), 176 deletions(-) create mode 100644 tools/blender/mitsuba/export/adjustments.py create mode 100644 tools/blender/mitsuba/properties/material.py create mode 100644 tools/blender/mitsuba/properties/texture.py create mode 100644 tools/blender/mitsuba/ui/materials/__init__.py create mode 100644 tools/blender/mitsuba/ui/materials/lambertian.py create mode 100644 tools/blender/mitsuba/ui/materials/main.py create mode 100644 tools/blender/mitsuba/ui/textures/__init__.py create mode 100644 tools/blender/mitsuba/ui/textures/checkerboard.py create mode 100644 tools/blender/mitsuba/ui/textures/ldrtexture.py create mode 100644 tools/blender/mitsuba/ui/textures/main.py diff --git a/src/textures/checkerboard.cpp b/src/textures/checkerboard.cpp index 3a81cf64..f8595556 100644 --- a/src/textures/checkerboard.cpp +++ b/src/textures/checkerboard.cpp @@ -29,29 +29,29 @@ MTS_NAMESPACE_BEGIN class Checkerboard : public Texture2D { public: Checkerboard(const Properties &props) : Texture2D(props) { - m_brightReflectance = props.getSpectrum("brightReflectance", Spectrum(.4f)); - m_darkReflectance = props.getSpectrum("darkReflectance", Spectrum(.2f)); + m_brightColor.g = props.getSpectrum("brightReflectance", Spectrum(.4f)); + m_darkColor.g = props.getSpectrum("darkReflectance", Spectrum(.2f)); } Checkerboard(Stream *stream, InstanceManager *manager) : Texture2D(stream, manager) { - m_brightReflectance = Spectrum(stream); - m_darkReflectance = Spectrum(stream); + m_brightColor.g = Spectrum(stream); + m_darkColor.g = Spectrum(stream); } void serialize(Stream *stream, InstanceManager *manager) const { Texture2D::serialize(stream, manager); - m_brightReflectance.serialize(stream); - m_darkReflectance.serialize(stream); + m_brightColor.g.serialize(stream); + m_darkColor.g.serialize(stream); } inline Spectrum getValue(const Point2 &uv) const { int x = 2*(((int) uv.x) % 2) - 1, y = 2*(((int) uv.y) % 2) - 1; if (x*y == 1) - return m_brightReflectance; + return m_brightColor.g; else - return m_darkReflectance; + return m_darkColor.g; } Spectrum getValue(const Point2 &uv, Float dudx, Float dudy, Float dvdx, Float dvdy) const { @@ -63,11 +63,11 @@ public: } Spectrum getAverage() const { - return m_darkReflectance * .5f; + return m_darkColor.g * .5f; } Spectrum getMaximum() const { - return m_brightReflectance; + return m_brightColor.g; } std::string toString() const { @@ -78,26 +78,26 @@ public: MTS_DECLARE_CLASS() protected: - Spectrum m_darkReflectance; - Spectrum m_brightReflectance; + Spectrum m_darkColor.g; + Spectrum m_brightColor.g; }; // ================ Hardware shader implementation ================ class CheckerboardShader : public Shader { public: - CheckerboardShader(Renderer *renderer, const Spectrum &brightReflectance, - const Spectrum &darkReflectance, const Point2 &uvOffset, + CheckerboardShader(Renderer *renderer, const Spectrum &brightColor.g, + const Spectrum &darkColor.g, const Point2 &uvOffset, const Vector2 &uvScale) : Shader(renderer, ETextureShader), - m_brightReflectance(brightReflectance), m_darkReflectance(darkReflectance), + m_brightColor.g(brightReflectance), m_darkReflectance(darkReflectance), m_uvOffset(uvOffset), m_uvScale(uvScale) { } void generateCode(std::ostringstream &oss, const std::string &evalName, const std::vector &depNames) const { - oss << "uniform vec3 " << evalName << "_brightReflectance;" << endl - << "uniform vec3 " << evalName << "_darkReflectance;" << endl + oss << "uniform vec3 " << evalName << "_brightColor.g;" << endl + << "uniform vec3 " << evalName << "_darkColor.g;" << endl << "uniform vec2 " << evalName << "_uvOffset;" << endl << "uniform vec2 " << evalName << "_uvScale;" << endl << endl @@ -107,37 +107,37 @@ public: << " uv.y * " << evalName << "_uvScale.y + " << evalName << "_uvOffset.y);" << endl << " float x = 2*(mod(int(uv.x), 2)) - 1, y = 2*(mod(int(uv.y), 2)) - 1;" << endl << " if (x*y == 1)" << endl - << " return " << evalName << "_brightReflectance;" << endl + << " return " << evalName << "_brightColor.g;" << endl << " else" << endl - << " return " << evalName << "_darkReflectance;" << endl + << " return " << evalName << "_darkColor.g;" << endl << "}" << endl; } void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { - parameterIDs.push_back(program->getParameterID(evalName + "_brightReflectance", false)); - parameterIDs.push_back(program->getParameterID(evalName + "_darkReflectance", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_brightColor.g", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_darkColor.g", false)); parameterIDs.push_back(program->getParameterID(evalName + "_uvOffset", false)); parameterIDs.push_back(program->getParameterID(evalName + "_uvScale", false)); } void bind(GPUProgram *program, const std::vector ¶meterIDs, int &textureUnitOffset) const { - program->setParameter(parameterIDs[0], m_brightReflectance); - program->setParameter(parameterIDs[1], m_darkReflectance); + program->setParameter(parameterIDs[0], m_brightColor.g); + program->setParameter(parameterIDs[1], m_darkColor.g); program->setParameter(parameterIDs[2], m_uvOffset); program->setParameter(parameterIDs[3], m_uvScale); } MTS_DECLARE_CLASS() private: - Spectrum m_brightReflectance; - Spectrum m_darkReflectance; + Spectrum m_brightColor.g; + Spectrum m_darkColor.g; Point2 m_uvOffset; Vector2 m_uvScale; }; Shader *Checkerboard::createShader(Renderer *renderer) const { - return new CheckerboardShader(renderer, m_brightReflectance, m_darkReflectance, + return new CheckerboardShader(renderer, m_brightColor.g, m_darkReflectance, m_uvOffset, m_uvScale); } diff --git a/src/textures/gridtexture.cpp b/src/textures/gridtexture.cpp index c28295bc..1f798d90 100644 --- a/src/textures/gridtexture.cpp +++ b/src/textures/gridtexture.cpp @@ -29,22 +29,22 @@ MTS_NAMESPACE_BEGIN class GridTexture : public Texture2D { public: GridTexture(const Properties &props) : Texture2D(props) { - m_brightReflectance = props.getSpectrum("brightReflectance", Spectrum(.4f)); - m_darkReflectance = props.getSpectrum("darkReflectance", Spectrum(.2f)); + m_brightColor = props.getSpectrum("brightColor", Spectrum(.4f)); + m_darkColor = props.getSpectrum("darkColor", Spectrum(.2f)); m_width = props.getFloat("width", .01f); } GridTexture(Stream *stream, InstanceManager *manager) : Texture2D(stream, manager) { - m_brightReflectance = Spectrum(stream); - m_darkReflectance = Spectrum(stream); + m_brightColor = Spectrum(stream); + m_darkColor = Spectrum(stream); m_width = stream->readFloat(); } void serialize(Stream *stream, InstanceManager *manager) const { Texture2D::serialize(stream, manager); - m_brightReflectance.serialize(stream); - m_darkReflectance.serialize(stream); + m_brightColor.serialize(stream); + m_darkColor.serialize(stream); stream->writeFloat(m_width); } @@ -58,9 +58,9 @@ public: y-=1; if (std::abs(x) < m_width || std::abs(y) < m_width) - return m_darkReflectance; + return m_darkColor; else - return m_brightReflectance; + return m_brightColor; } Spectrum getValue(const Point2 &uv, Float dudx, @@ -73,11 +73,11 @@ public: } Spectrum getMaximum() const { - return m_brightReflectance; + return m_brightColor; } Spectrum getAverage() const { - return m_brightReflectance; // that's not quite right + return m_brightColor; // that's not quite right } std::string toString() const { @@ -88,8 +88,8 @@ public: MTS_DECLARE_CLASS() protected: - Spectrum m_brightReflectance; - Spectrum m_darkReflectance; + Spectrum m_brightColor; + Spectrum m_darkColor; Float m_width; }; @@ -97,18 +97,18 @@ protected: class GridTextureShader : public Shader { public: - GridTextureShader(Renderer *renderer, const Spectrum &brightReflectance, - const Spectrum &darkReflectance, Float width, const Point2 &uvOffset, + GridTextureShader(Renderer *renderer, const Spectrum &brightColor, + const Spectrum &darkColor, Float width, const Point2 &uvOffset, const Vector2 &uvScale) : Shader(renderer, ETextureShader), - m_brightReflectance(brightReflectance), m_darkReflectance(darkReflectance), + m_brightColor(brightColor), m_darkColor(darkColor), m_width(width), m_uvOffset(uvOffset), m_uvScale(uvScale) { } void generateCode(std::ostringstream &oss, const std::string &evalName, const std::vector &depNames) const { - oss << "uniform vec3 " << evalName << "_brightReflectance;" << endl - << "uniform vec3 " << evalName << "_darkReflectance;" << endl + oss << "uniform vec3 " << evalName << "_brightColor;" << endl + << "uniform vec3 " << evalName << "_darkColor;" << endl << "uniform float " << evalName << "_width;" << endl << "uniform vec2 " << evalName << "_uvOffset;" << endl << "uniform vec2 " << evalName << "_uvScale;" << endl @@ -122,15 +122,15 @@ public: << " if (x > .5) x -= 1.0;" << endl << " if (y > .5) y -= 1.0;" << endl << " if (abs(x) < " << evalName << "_width || abs(y) < " << evalName << "_width)" << endl - << " return " << evalName << "_darkReflectance;" << endl + << " return " << evalName << "_darkColor;" << endl << " else" << endl - << " return " << evalName << "_brightReflectance;" << endl + << " return " << evalName << "_brightColor;" << endl << "}" << endl; } void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { - parameterIDs.push_back(program->getParameterID(evalName + "_brightReflectance", false)); - parameterIDs.push_back(program->getParameterID(evalName + "_darkReflectance", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_brightColor", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_darkColor", false)); parameterIDs.push_back(program->getParameterID(evalName + "_width", false)); parameterIDs.push_back(program->getParameterID(evalName + "_uvOffset", false)); parameterIDs.push_back(program->getParameterID(evalName + "_uvScale", false)); @@ -138,8 +138,8 @@ public: void bind(GPUProgram *program, const std::vector ¶meterIDs, int &textureUnitOffset) const { - program->setParameter(parameterIDs[0], m_brightReflectance); - program->setParameter(parameterIDs[1], m_darkReflectance); + program->setParameter(parameterIDs[0], m_brightColor); + program->setParameter(parameterIDs[1], m_darkColor); program->setParameter(parameterIDs[2], m_width); program->setParameter(parameterIDs[3], m_uvOffset); program->setParameter(parameterIDs[4], m_uvScale); @@ -147,15 +147,15 @@ public: MTS_DECLARE_CLASS() private: - Spectrum m_brightReflectance; - Spectrum m_darkReflectance; + Spectrum m_brightColor; + Spectrum m_darkColor; Float m_width; Point2 m_uvOffset; Vector2 m_uvScale; }; Shader *GridTexture::createShader(Renderer *renderer) const { - return new GridTextureShader(renderer, m_brightReflectance, m_darkReflectance, + return new GridTextureShader(renderer, m_brightColor, m_darkColor, m_width, m_uvOffset, m_uvScale); } diff --git a/tools/blender/mitsuba/core/__init__.py b/tools/blender/mitsuba/core/__init__.py index 5894ed90..ddfdcbc8 100644 --- a/tools/blender/mitsuba/core/__init__.py +++ b/tools/blender/mitsuba/core/__init__.py @@ -29,12 +29,19 @@ 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.properties.texture import mitsuba_texture, \ + mitsuba_tex_ldrtexture, mitsuba_tex_checkerboard +from mitsuba.properties.material import mitsuba_material, \ + mitsuba_mat_lambertian + 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 +from mitsuba.ui.textures import main, ldrtexture, checkerboard +from mitsuba.ui.materials import main, lambertian def compatible(mod): mod = __import__(mod) @@ -45,7 +52,6 @@ def compatible(mod): pass del mod - import properties_data_lamp properties_data_lamp.DATA_PT_context_lamp.COMPAT_ENGINES.add('mitsuba') del properties_data_lamp @@ -56,6 +62,10 @@ properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('mitsuba') properties_render.RENDER_PT_output.COMPAT_ENGINES.add('mitsuba') del properties_render +import properties_texture +properties_texture.TEXTURE_PT_context_texture.COMPAT_ENGINES.add('mitsuba') +del properties_texture + compatible("properties_data_mesh") compatible("properties_data_camera") @@ -65,7 +75,12 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base): property_groups = [ ('Scene', mitsuba_engine), - ('Lamp', mitsuba_lamp) + ('Lamp', mitsuba_lamp), + ('Texture', mitsuba_texture), + ('mitsuba_texture', mitsuba_tex_ldrtexture), + ('mitsuba_texture', mitsuba_tex_checkerboard), + ('Material', mitsuba_material), + ('mitsuba_material', mitsuba_mat_lambertian) ] render_lock = threading.Lock() @@ -142,7 +157,7 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base): 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 we exit the wait loop (user cancelled) and mitsuba is still running, then send SIGINT if mitsuba_process.poll() == None: # Use SIGTERM because that's the only one supported on Windows mitsuba_process.send_signal(subprocess.signal.SIGTERM) @@ -152,6 +167,7 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine, engine_base): framebuffer_thread.join() if mitsuba_process.poll() != None and mitsuba_process.returncode != 0: + MtsLog("MtsBlend: Rendering failed -- check the console") bpy.ops.ef.msg(msg_type='ERROR', msg_text='Rendering failed -- check the console.') else: framebuffer_thread.kick(render_end=True) diff --git a/tools/blender/mitsuba/export/__init__.py b/tools/blender/mitsuba/export/__init__.py index 711041e8..c8224846 100644 --- a/tools/blender/mitsuba/export/__init__.py +++ b/tools/blender/mitsuba/export/__init__.py @@ -16,105 +16,84 @@ # # ##### END GPL LICENSE BLOCK ##### -import os, math, mathutils +import os -from extensions_framework import util as efutil +class ParamSetItem(list): + type = None + type_name = None + name = None + value = None -class MtsAdjustments: - ''' - 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, *args): + self.type, self.name, self.value = args + self.type_name = "%s %s" % (self.type, self.name) + self.append(self.type_name) + self.append(self.value) + + def to_string(self): + if self.type == "color": + return '\t\t\n' % (self.name, + self.value[0], self.value[1], self.value[2]) + elif self.type == "point" or self.type == "vector": + return '\t\t<%s name="%s" value="%s %s %s"/>\n' % (self.type, + self.name, self.value[0], self.value[1], self.value[2]) + elif self.type == "integer" or self.type == "float" \ + or self.type == "string": + return '\t\t<%s name="%s" value="%s"/>\n' % (self.type, self.name, self.value) + elif self.type == "reference_texture" or self.type == "reference_material": + return '\t\t\n' % (self.name, self.value) - def __init__(self, target_file, target_dir): - self.target_file = target_file - self.target_dir = target_dir +class ParamSet(list): + names = [] + + def update(self, other): + for p in other: + self.add(p.type, p.name, p.value) + return self + + def add(self, type, name, value): + if name in self.names: + for p in self: + if p.name == name: + self.remove(p) + + self.append( + ParamSetItem(type, name, value) + ) + self.names.append(name) + return self + + def add_float(self, name, value): + self.add('float', name, value) + return self + + def add_integer(self, name, value): + self.add('integer', name, value) + return self - def export_worldtrafo(self, adjfile, trafo): - adjfile.write('\t\t\n') - adjfile.write('\t\t\t\n\t\t\n') + def add_reference(self, type, name, value): + self.add('reference_%s' % type, name, value) + return self - def export_lamp(self, adjfile, lamp, idx): - ltype = lamp.data.mitsuba_lamp.type - if ltype == 'POINT': - adjfile.write('\t\n' % lamp.data.name) - mult = lamp.data.mitsuba_lamp.intensity - self.export_worldtrafo(adjfile, lamp.matrix_world) - adjfile.write('\t\t\n' - % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) - adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) - adjfile.write('\t\n') - elif ltype == 'AREA': - adjfile.write('\t\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.target_dir, 'meshes'), "_area_luminaire_%d.obj" % idx) + def add_bool(self, name, value): + self.add('bool', name, bool(value)) + return self - adjfile.write('\t\t\n' % path) - self.export_worldtrafo(adjfile, lamp.matrix_world) - - adjfile.write('\n\t\t\n' % lamp.data.name) - mult = lamp.data.mitsuba_lamp.intensity / (2 * size_x * size_y) - adjfile.write('\t\t\t\n' - % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) - adjfile.write('\t\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) - adjfile.write('\t\t\n') - adjfile.write('\t\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() - elif ltype == 'SUN': - adjfile.write('\t\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\n' - % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) - adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) - adjfile.write('\t\n') - elif ltype == 'SPOT': - adjfile.write('\t\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\n' - % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) - adjfile.write('\t\t\n' % (lamp.data.spot_size * 180 / (math.pi * 2))) - adjfile.write('\t\t\n' % (lamp.data.spot_blend * lamp.data.spot_size * 180 / (math.pi * 2))) - adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) - adjfile.write('\t\n') - elif ltype == 'ENV': - if lamp.data.mitsuba_lamp.envmap_type == 'constant': - adjfile.write('\t\n' % lamp.data.name) - mult = lamp.data.mitsuba_lamp.intensity - adjfile.write('\t\t\n' - % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) - adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) - adjfile.write('\t\n') - elif lamp.data.mitsuba_lamp.envmap_type == 'envmap': - adjfile.write('\t\n' % lamp.data.name) - adjfile.write('\t\t\n' % efutil.filesystem_path(lamp.data.mitsuba_lamp.envmap_file)) - self.export_worldtrafo(adjfile, lamp.matrix_world) - adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.intensity) - adjfile.write('\t\n') - - def export(self, scene): - adjfile = open(self.target_file, 'w') - adjfile.write('\n'); - idx = 0 - for obj in scene.objects: - if obj.type == 'LAMP': - self.export_lamp(adjfile, obj, idx) - idx = idx+1 - adjfile.write('\n'); - adjfile.close() + def add_string(self, name, value): + self.add('string', name, str(value)) + return self + + def add_vector(self, name, value): + self.add('vector', name, [i for i in value]) + return self + + def add_point(self, name, value): + self.add('point', name, [p for p in value]) + return self + + def add_color(self, name, value): + self.add('color', name, [c for c in value]) + return self + + def to_string(self): + return ''.join(item.to_string() for item in self) diff --git a/tools/blender/mitsuba/export/adjustments.py b/tools/blender/mitsuba/export/adjustments.py new file mode 100644 index 00000000..3a64583b --- /dev/null +++ b/tools/blender/mitsuba/export/adjustments.py @@ -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 ##### + +import os, math, mathutils + +import bpy + +from extensions_framework import util as efutil + +class MtsAdjustments: + ''' + 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 + self.exported_materials = [] + self.exported_textures = [] + + def export_worldtrafo(self, adjfile, trafo): + adjfile.write('\t\t\n') + adjfile.write('\t\t\t\n\t\t\n') + + def export_lamp(self, adjfile, lamp, idx): + ltype = lamp.data.mitsuba_lamp.type + if ltype == 'POINT': + adjfile.write('\t\n' % lamp.data.name) + mult = lamp.data.mitsuba_lamp.intensity + self.export_worldtrafo(adjfile, lamp.matrix_world) + adjfile.write('\t\t\n' + % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) + adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) + adjfile.write('\t\n') + elif ltype == 'AREA': + adjfile.write('\t\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.target_dir, 'meshes'), "_area_luminaire_%d.obj" % idx) + + adjfile.write('\t\t\n' % path) + self.export_worldtrafo(adjfile, lamp.matrix_world) + + adjfile.write('\n\t\t\n' % lamp.data.name) + mult = lamp.data.mitsuba_lamp.intensity / (2 * size_x * size_y) + adjfile.write('\t\t\t\n' + % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) + adjfile.write('\t\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) + adjfile.write('\t\t\n') + adjfile.write('\t\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() + elif ltype == 'SUN': + adjfile.write('\t\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\n' + % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) + adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) + adjfile.write('\t\n') + elif ltype == 'SPOT': + adjfile.write('\t\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\n' + % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) + adjfile.write('\t\t\n' % (lamp.data.spot_size * 180 / (math.pi * 2))) + adjfile.write('\t\t\n' % (lamp.data.spot_blend * lamp.data.spot_size * 180 / (math.pi * 2))) + adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) + adjfile.write('\t\n') + elif ltype == 'ENV': + if lamp.data.mitsuba_lamp.envmap_type == 'constant': + adjfile.write('\t\n' % lamp.data.name) + mult = lamp.data.mitsuba_lamp.intensity + adjfile.write('\t\t\n' + % (lamp.data.color.r*mult, lamp.data.color.g*mult, lamp.data.color.b*mult)) + adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.sampling_weight) + adjfile.write('\t\n') + elif lamp.data.mitsuba_lamp.envmap_type == 'envmap': + adjfile.write('\t\n' % lamp.data.name) + adjfile.write('\t\t\n' % efutil.filesystem_path(lamp.data.mitsuba_lamp.envmap_file)) + self.export_worldtrafo(adjfile, lamp.matrix_world) + adjfile.write('\t\t\n' % lamp.data.mitsuba_lamp.intensity) + adjfile.write('\t\n') + + def find_texture(self, name): + if name in bpy.data.textures: + return bpy.data.textures[name] + else: + raise Exception('Failed to find texture "%s"' % name) + + def find_material(self, name): + if name in bpy.data.materials: + return bpy.data.materials[name] + else: + raise Exception('Failed to find material "%s"' % name) + + def export_texture(self, adjfile, mat): + if mat.name in self.exported_textures: + return + self.exported_textures += [mat.name] + params = mat.mitsuba_texture.get_params() + + for p in params: + if p.type == 'reference_texture': + self.export_texture(adjfile, self.find_texture(p.value)) + + adjfile.write('\t\n' % (mat.name, mat.mitsuba_texture.type)) + adjfile.write(params.to_string()) + adjfile.write('\t\n') + + def export_material(self, adjfile, mat): + if mat.name in self.exported_materials: + return + self.exported_materials += [mat.name] + params = mat.mitsuba_material.get_params() + + for p in params: + if p.type == 'reference_material': + self.export_material(adjfile, self.find_material(p.value)) + elif p.type == 'reference_texture': + self.export_texture(adjfile, self.find_texture(p.value)) + + adjfile.write('\t\n' % (mat.name, mat.mitsuba_material.type)) + adjfile.write(params.to_string()) + adjfile.write('\t\n') + + def export(self, scene): + adjfile = open(self.target_file, 'w') + adjfile.write('\n'); + idx = 0 + for obj in scene.objects: + if obj.type == 'LAMP': + self.export_lamp(adjfile, obj, idx) + elif obj.type == 'MESH': + for mat in obj.data.materials: + self.export_material(adjfile, mat) + idx = idx+1 + adjfile.write('\n'); + adjfile.close() + + + diff --git a/tools/blender/mitsuba/operators/__init__.py b/tools/blender/mitsuba/operators/__init__.py index 36c93510..6aec1d1e 100644 --- a/tools/blender/mitsuba/operators/__init__.py +++ b/tools/blender/mitsuba/operators/__init__.py @@ -27,7 +27,7 @@ from presets import AddPresetBase from extensions_framework import util as efutil from mitsuba.outputs import MtsLog -from mitsuba.export import MtsAdjustments +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) @@ -59,32 +59,61 @@ class MITSUBA_OT_preset_engine_add(MITSUBA_OT_preset_base, bpy.types.Operator): ] 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) - MtsLog("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") +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): - scene = bpy.data.scenes[0] - for obj in scene.objects: - if obj.type == 'LAMP': - self._check_lamp(obj.data) - return {'FINISHED'} + 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() + ]) + + 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() + ]) + + self.preset_values = pv + return super().execute(context) class EXPORT_OT_mitsuba(bpy.types.Operator): bl_idname = 'export.mitsuba' diff --git a/tools/blender/mitsuba/properties/material.py b/tools/blender/mitsuba/properties/material.py new file mode 100644 index 00000000..54fb4c88 --- /dev/null +++ b/tools/blender/mitsuba/properties/material.py @@ -0,0 +1,66 @@ +import math +from copy import deepcopy + +import bpy + +from extensions_framework import declarative_property_group +from extensions_framework import util as efutil +from mitsuba.properties.texture import TextureParameter +from mitsuba.export import ParamSet + +param_reflectance = TextureParameter('reflectance', 'Reflectance', \ + 'Diffuse reflectance value', default=(0.5, 0.5, 0.5)) + +def dict_merge(*args): + vis = {} + for vis_dict in args: + vis.update(deepcopy(vis_dict)) + return vis + + +class mitsuba_material(declarative_property_group): + ''' + Storage class for Mitsuba Material settings. + This class will be instantiated within a Blender Material + object. + ''' + + controls = [ + 'type', + ] + + properties = [ + # Material Type Select + { + 'type': 'enum', + 'attr': 'type', + 'name': 'Type', + 'description': 'Mitsuba material type', + 'default': 'matte', + 'items': [ + ('lambertian', 'Lambertian', 'Lambertian (i.e. ideally diffuse) material') + ], + 'save_in_preset': True + } + ] + + def get_params(self): + sub_type = getattr(self, 'mitsuba_mat_%s' % self.type) + return sub_type.get_params() + +class mitsuba_mat_lambertian(declarative_property_group): + controls = [ + ] + param_reflectance.controls + + properties = [ + ] + param_reflectance.properties + + visibility = dict_merge( + param_reflectance.visibility + ) + + def get_params(self): + params = ParamSet() + params.update(param_reflectance.get_params(self)) + return params + diff --git a/tools/blender/mitsuba/properties/texture.py b/tools/blender/mitsuba/properties/texture.py new file mode 100644 index 00000000..a6f13443 --- /dev/null +++ b/tools/blender/mitsuba/properties/texture.py @@ -0,0 +1,368 @@ +# ##### 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 +from extensions_framework.validate import Logic_OR as O + +from mitsuba.export import ParamSet + +#------------------------------------------------------------------------------ +# Texture property group construction helpers +#------------------------------------------------------------------------------ + +class TextureParameterBase(object): + attr = None + name = None + default = (0.8, 0.8, 0.8) + min = 0.0 + max = 1.0 + + texture_collection = 'texture_slots' + + controls = None + visibility = None + properties = None + + def __init__(self, attr, name, description, default=None, min=None, max=None): + self.attr = attr + self.name = name + self.description = description + if default is not None: + self.default = default + if min is not None: + self.min = min + if max is not None: + self.max = max + + self.controls = self.get_controls() + self.visibility = self.get_visibility() + self.properties = self.get_properties() + + def texture_collection_finder(self): + return lambda s,c: s.object.material_slots[s.object.active_material_index].material + + def texture_slot_set_attr(self): + def set_attr(s,c): + if type(c).__name__ == 'mitsuba_material': + return getattr(c, 'mitsuba_mat_%s'%c.type) + else: + return getattr(c, 'mitsuba_tex_%s'%c.type) + return set_attr + + def get_controls(self): + ''' + Subclasses can override this for their own needs + ''' + return [] + + def get_visibility(self): + ''' + Subclasses can override this for their own needs + ''' + return {} + + def get_properties(self): + ''' + Subclasses can override this for their own needs + ''' + return [] + + def get_extra_controls(self): + ''' + Subclasses can override this for their own needs + ''' + return [] + + def get_extra_visibility(self): + ''' + Subclasses can override this for their own needs + ''' + return {} + + def get_extra_properties(self): + ''' + Subclasses can override this for their own needs + ''' + return [] + + def get_params(self, context): + ''' + Return a Mitsuba ParamSet of the properties + defined in this Texture, getting parameters + from property group 'context' + ''' + return ParamSet() + +class TextureParameter(TextureParameterBase): + def get_controls(self): + return [ + [ 0.9, [0.375,'%s_colorlabel' % self.attr, '%s_color' % self.attr], '%s_usetexture' % self.attr ], + '%s_texture' % self.attr + ] + self.get_extra_controls() + + def get_visibility(self): + vis = { + '%s_texture' % self.attr: { '%s_usetexture' % self.attr: True }, + } + vis.update(self.get_extra_visibility()) + return vis + + # colour for each material type. If the property name is + # not set, then the color won't be changed. + master_color_map = { + 'lambertian': 'reflectance' + } + + def set_master_colour(self, s, c): + ''' + This neat little hack will set the blender material colour to the value + given in the material panel via the property's 'draw' lambda function. + ''' + + if c.type in self.master_color_map.keys() and self.attr == self.master_color_map[c.type]: + submat = getattr(c, 'mitsuba_mat_%s'%c.type) + submat_col = getattr(submat, self.attr+'_color') + if s.material.diffuse_color != submat_col: + s.material.diffuse_color = submat_col + + def get_properties(self): + return [ + { + 'attr': self.attr, + 'type': 'string', + 'default': 'mts_color_texture' + }, + { + 'attr': '%s_usetexture' % self.attr, + 'type': 'bool', + 'name': 'T', + 'description': 'Textured %s' % self.name, + 'default': False, + 'toggle': True, + 'save_in_preset': True + }, + { + 'type': 'text', + 'attr': '%s_colorlabel' % self.attr, + 'name': self.name + }, + { + 'type': 'float_vector', + 'attr': '%s_color' % self.attr, + 'name': '', #self.name, + 'description': self.description, + 'default': self.default, + 'min': self.min, + 'soft_min': self.min, + 'max': self.max, + 'soft_max': self.max, + 'subtype': 'COLOR', + 'draw': lambda s,c: self.set_master_colour(s, c), + 'save_in_preset': True + }, + { + 'attr': '%s_texturename' % self.attr, + 'type': 'string', + 'name': '%s_texturename' % self.attr, + 'description': '%s Texture' % self.name, + 'save_in_preset': True + }, + { + 'type': 'prop_search', + 'attr': '%s_texture' % self.attr, + 'src': self.texture_collection_finder(), + 'src_attr': self.texture_collection, + 'trg': self.texture_slot_set_attr(), + 'trg_attr': '%s_texturename' % self.attr, + 'name': self.name + } + ] + self.get_extra_properties() + + def get_params(self, context): + params = ParamSet() + if hasattr(context, '%s_usetexture' % self.attr) \ + and getattr(context, '%s_usetexture' % self.attr): + params.add_reference('texture', self.attr, getattr(context, '%s_texturename' % self.attr)) + else: + params.add_color( + self.attr, + getattr(context, '%s_color' % self.attr) + ) + return params + +class mitsuba_texture(declarative_property_group): + ''' + Storage class for Mitsuba Texture settings. + This class will be instantiated within a Blender Texture + object. + ''' + + controls = [ + 'type' + ] + + properties = [ + { + 'attr': 'type', + 'name': 'Texture type', + 'type': 'enum', + 'items': [ + ('ldrtexture', 'Bitmap', 'ldrtexture'), + ('checkerboard', 'Checkerboard', 'checkerboard') + ], + 'default' : 'ldrtexture', + 'save_in_preset': True + }, + ] + + def get_params(self): + if hasattr(self, 'mitsuba_tex_%s' % self.type): + mts_texture = getattr(self, 'mitsuba_tex_%s' % self.type) + return mts_texture.get_params() + else: + return ParamSet() + +class mitsuba_tex_ldrtexture(declarative_property_group): + controls = [ + 'filename', + 'filtertype', + 'gamma', + 'max_anisotropy', + 'wrap', + ] + + visibility = { + 'max_anisotropy': { 'filtertype': 'ewa' }, + 'gamma': { 'srgb': False } + } + + properties = [ + { + 'type': 'string', + 'subtype': 'FILE_PATH', + 'attr': 'filename', + 'description' : 'Path to a PNG/JPG/TGA/BMP file', + 'name': 'File Name', + 'save_in_preset': True + }, + { + 'type': 'enum', + 'attr': 'filtertype', + 'name': 'Filter type', + 'description' : 'Specifies the type of texture filtering', + 'items': [ + ('isotropic', 'Isotropic (Trilinear MipMap)', 'isotropic'), + ('ewa', 'Anisotropic (EWA Filtering)', 'ewa') + ], + 'default' : 'ewa', + 'save_in_preset': True + }, + { + 'type': 'bool', + 'attr': 'srgb', + 'name': 'sRGB', + 'description' : 'Is the texture stored in sRGB color space?', + 'default': True, + 'save_in_preset': True + }, + { + 'type': 'float', + 'attr': 'gamma', + 'name': 'Gamma', + 'description' : 'Specifies the texture gamma value', + 'default': 2.2, + 'min': 0.01, + 'soft_min': 0.01, + 'max': 6.0, + 'soft_max': 6.0, + 'save_in_preset': True + }, + { + 'type': 'float', + 'description' : 'Maximum allowed anisotropy when using the EWA filter', + 'attr': 'max_anisotropy', + 'name': 'Max. Anisotropy', + 'default': 8.0, + 'save_in_preset': True + }, + { + 'type': 'enum', + 'attr': 'wrap', + 'name': 'Wrapping', + 'description' : 'What should be done when encountering UV coordinates outside of the range [0,1]', + 'items': [ + ('repeat', 'Repeat', 'repeat'), + ('black', 'Black outside of [0, 1]', 'black'), + ('white', 'White outside of [0, 1]', 'white'), + ('clamp', 'Clamp to edges', 'clamp') + ], + 'save_in_preset': True + } + ] + + def get_params(self): + params = ParamSet() + + params.add_string('filename', efutil.path_relative_to_export(self.filename) ) \ + .add_string('filtertype', self.filtertype) \ + .add_float('max_anisotropy', self.max_anisotropy) \ + .add_string('wrap', self.wrap) \ + .add_float('gamma', -1 if self.srgb else self.gamma) + + return params + +class mitsuba_tex_checkerboard(declarative_property_group): + controls = [ + 'darkColor', + 'brightColor' + ] + + properties = [ + { + 'attr': 'darkColor', + 'type': 'float_vector', + 'subtype': 'COLOR', + 'name' : 'Dark color', + 'description' : 'Color of the dark patches', + 'default' : (0.2, 0.2, 0.2), + 'min': 0.0, + 'max': 1.0, + 'save_in_preset': True + }, + { + 'attr': 'brightColor', + 'type': 'float_vector', + 'subtype': 'COLOR', + 'description' : 'Color of the bright patches', + 'name' : 'Bright color', + 'default' : (0.4, 0.4, 0.4), + 'min': 0.0, + 'max': 1.0, + 'save_in_preset': True + } + ] + + def get_params(self): + params = ParamSet() + + params.add_color('darkColor', self.darkColor) + params.add_color('brightColor', self.brightColor) + + return params + diff --git a/tools/blender/mitsuba/ui/lamps.py b/tools/blender/mitsuba/ui/lamps.py index e4e594c0..42487ffe 100644 --- a/tools/blender/mitsuba/ui/lamps.py +++ b/tools/blender/mitsuba/ui/lamps.py @@ -79,14 +79,13 @@ class lamps(DataButtonsPanel, property_group_renderer, bpy.types.Panel): # AREA LAMP: Blender Properties elif lamp.type == 'AREA': - if wide_ui: col=layout.row() else: col=layout.column() col.row().prop(lamp, "shape", expand=True) - sub = col.column(align=True) + if (lamp.shape == 'SQUARE'): sub.prop(lamp, "size") elif (lamp.shape == 'RECTANGLE'): diff --git a/tools/blender/mitsuba/ui/materials/__init__.py b/tools/blender/mitsuba/ui/materials/__init__.py new file mode 100644 index 00000000..0d869991 --- /dev/null +++ b/tools/blender/mitsuba/ui/materials/__init__.py @@ -0,0 +1,37 @@ +# ##### 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_material import MaterialButtonsPanel + +from extensions_framework.ui import property_group_renderer + +class mitsuba_material_base(MaterialButtonsPanel, property_group_renderer): + COMPAT_ENGINES = {'mitsuba'} + +class mitsuba_material_sub(MaterialButtonsPanel, property_group_renderer): + COMPAT_ENGINES = {'mitsuba'} + MTS_COMPAT = set() + + @classmethod + def poll(cls, context): + ''' + Only show Mitsuba panel if mitsuba_material.material in MTS_COMPAT + ''' + + return super().poll(context) and context.material.mitsuba_material.type in cls.MTS_COMPAT diff --git a/tools/blender/mitsuba/ui/materials/lambertian.py b/tools/blender/mitsuba/ui/materials/lambertian.py new file mode 100644 index 00000000..c3deedf0 --- /dev/null +++ b/tools/blender/mitsuba/ui/materials/lambertian.py @@ -0,0 +1,30 @@ +# ##### 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 mitsuba.ui.materials import mitsuba_material_sub + +class ui_material_lambertian(mitsuba_material_sub, bpy.types.Panel): + bl_label = 'Mitsuba Lambertian Material' + + MTS_COMPAT = {'lambertian'} + + display_property_groups = [ + ( ('material', 'mitsuba_material'), 'mitsuba_mat_lambertian' ) + ] diff --git a/tools/blender/mitsuba/ui/materials/main.py b/tools/blender/mitsuba/ui/materials/main.py new file mode 100644 index 00000000..7e588ea0 --- /dev/null +++ b/tools/blender/mitsuba/ui/materials/main.py @@ -0,0 +1,40 @@ +# ##### 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 mitsuba.ui.materials import mitsuba_material_base + +class main(mitsuba_material_base, bpy.types.Panel): + ''' + Material Editor UI Panel + ''' + + bl_label = 'Mitsuba Materials' + + display_property_groups = [ + ( ('material',), 'mitsuba_material' ) + ] + + def draw(self, context): + row = self.layout.row(align=True) + row.menu("MITSUBA_MT_presets_material", text=bpy.types.MITSUBA_MT_presets_material.bl_label) + row.operator("mitsuba.preset_material_add", text="", icon="ZOOMIN") + row.operator("mitsuba.preset_material_add", text="", icon="ZOOMOUT").remove_active = True + + super().draw(context) diff --git a/tools/blender/mitsuba/ui/textures/__init__.py b/tools/blender/mitsuba/ui/textures/__init__.py new file mode 100644 index 00000000..9abbad5b --- /dev/null +++ b/tools/blender/mitsuba/ui/textures/__init__.py @@ -0,0 +1,41 @@ +# ##### 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_texture import TextureButtonsPanel + +from extensions_framework.ui import property_group_renderer + +class mitsuba_texture_base(TextureButtonsPanel, property_group_renderer): + ''' + This is the base class for all Mitsuba texture sub-panels. + ''' + + COMPAT_ENGINES = {'mitsuba'} + MTS_COMPAT = set() + + @classmethod + def poll(cls, context): + ''' + Only show panel if mitsuba_texture.type in MTS_COMPAT + ''' + tex = context.texture + return tex and \ + (context.scene.render.engine in cls.COMPAT_ENGINES) and \ + context.texture.mitsuba_texture.type in cls.MTS_COMPAT diff --git a/tools/blender/mitsuba/ui/textures/checkerboard.py b/tools/blender/mitsuba/ui/textures/checkerboard.py new file mode 100644 index 00000000..e6621ed0 --- /dev/null +++ b/tools/blender/mitsuba/ui/textures/checkerboard.py @@ -0,0 +1,30 @@ +# ##### 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 mitsuba.ui.textures import mitsuba_texture_base + +class ui_texture_checkerboard(mitsuba_texture_base, bpy.types.Panel): + bl_label = 'Mitsuba Checkerboard Texture' + + MTS_COMPAT = {'checkerboard'} + + display_property_groups = [ + ( ('texture', 'mitsuba_texture'), 'mitsuba_tex_checkerboard' ) + ] diff --git a/tools/blender/mitsuba/ui/textures/ldrtexture.py b/tools/blender/mitsuba/ui/textures/ldrtexture.py new file mode 100644 index 00000000..ad38aef1 --- /dev/null +++ b/tools/blender/mitsuba/ui/textures/ldrtexture.py @@ -0,0 +1,30 @@ +# ##### 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 mitsuba.ui.textures import mitsuba_texture_base + +class ui_texture_ldrtexture(mitsuba_texture_base, bpy.types.Panel): + bl_label = 'Mitsuba Bitmap Texture' + + MTS_COMPAT = {'ldrtexture'} + + display_property_groups = [ + ( ('texture', 'mitsuba_texture'), 'mitsuba_tex_ldrtexture' ) + ] diff --git a/tools/blender/mitsuba/ui/textures/main.py b/tools/blender/mitsuba/ui/textures/main.py new file mode 100644 index 00000000..ffad7f70 --- /dev/null +++ b/tools/blender/mitsuba/ui/textures/main.py @@ -0,0 +1,48 @@ +# ##### 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 mitsuba.ui.textures import mitsuba_texture_base + +class ui_texture_main(mitsuba_texture_base, bpy.types.Panel): + ''' + Texture Editor UI Panel + ''' + + bl_label = 'Mitsuba Textures' + + display_property_groups = [ + ( ('texture',), 'mitsuba_texture' ) + ] + + @classmethod + def poll(cls, context): + ''' + Only show Mitsuba panel with the correct texture type + ''' + tex = context.texture + return tex and (context.scene.render.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + row = self.layout.row(align=True) + row.menu("MITSUBA_MT_presets_texture", text=bpy.types.MITSUBA_MT_presets_texture.bl_label) + row.operator("mitsuba.preset_texture_add", text="", icon="ZOOMIN") + row.operator("mitsuba.preset_texture_add", text="", icon="ZOOMOUT").remove_active = True + + super().draw(context)