diff --git a/data/blender/mitsuba/core/__init__.py b/data/blender/mitsuba/core/__init__.py index ac6866b0..b700421b 100644 --- a/data/blender/mitsuba/core/__init__.py +++ b/data/blender/mitsuba/core/__init__.py @@ -106,8 +106,6 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine): output_dir = scene_path else: output_dir = os.path.dirname(scene_path) - if output_dir[-1] != '/': - output_dir += '/' efutil.export_path = output_dir os.chdir(output_dir) @@ -245,4 +243,3 @@ class RENDERENGINE_mitsuba(bpy.types.RenderEngine): framebuffer_thread.kick(render_end=True) framebuffer_thread.shutdown() - diff --git a/data/blender/mitsuba/export/__init__.py b/data/blender/mitsuba/export/__init__.py index 7a2d289d..dc35972c 100644 --- a/data/blender/mitsuba/export/__init__.py +++ b/data/blender/mitsuba/export/__init__.py @@ -401,7 +401,13 @@ class MtsExporter: if not hasattr(mat, 'name') or mat.name in self.exported_materials: return self.exported_materials += [mat.name] - params = mat.mitsuba_material.get_params() + mmat = mat.mitsuba_material + params = mmat.get_params() + twosided = False + + if mmat.twosided and mmat.type in ['lambertian', 'phong', 'ward', + 'mirror', 'roughmetal', 'microfacet', 'composite']: + twosided = True for p in params: if p.type == 'reference_material': @@ -409,14 +415,22 @@ class MtsExporter: elif p.type == 'reference_texture': self.exportTexture(self.findTexture(p.value)) - self.openElement('bsdf', {'id' : '%s-material' % translate_id(mat.name), 'type' : mat.mitsuba_material.type}) + if twosided: + self.openElement('bsdf', {'id' : '%s-material' % translate_id(mat.name), 'type' : 'twosided'}) + self.openElement('bsdf', {'type' : mmat.type}) + else: + self.openElement('bsdf', {'id' : '%s-material' % translate_id(mat.name), 'type' : mmat.type}) + params.export(self) self.closeElement() + + if twosided: + self.closeElement() def exportEmission(self, obj): - mult = lamp.intensity lamp = obj.data.materials[0].mitsuba_emission name = translate_id(obj.data.name) + mult = lamp.intensity self.openElement('append', { 'id' : '%s-mesh_0' % name}) self.openElement('luminaire', { 'id' : '%s-emission' % name, 'type' : 'area'}) self.parameter('float', 'samplingWeight', {'value' : '%f' % lamp.samplingWeight}) @@ -426,23 +440,21 @@ class MtsExporter: self.closeElement() def exportPreviewMesh(self, material): - self.out.write('\t\t\n') - self.out.write('\t\t\t\n') - self.out.write('\t\t\t\n') - self.out.write('\t\t\t\n') - self.out.write('\t\t\t\t\n') - self.out.write('\t\t\t\n') - self.out.write('\t\t\t\n' % translate_id(material.name)) + self.openElement('shape', {'id' : 'Exterior-mesh_0', 'type' : 'serialized'}) + self.parameter('string', 'filename', {'value' : 'matpreview.serialized'}) + self.parameter('integer', 'shapeIndex', {'value' : '1'}) + self.openElement('transform', {'name' : 'toWorld'}) + self.element('matrix', {'value' : '0.614046 0.614047 0 -1.78814e-07 -0.614047 0.614046 0 2.08616e-07 0 0 0.868393 1.02569 0 0 0 1'}) + self.closeElement() + self.element('ref', {'name' : 'bsdf', 'id' : '%s-material' % translate_id(material.name)}) lamp = material.mitsuba_emission if lamp and lamp.use_emission: mult = lamp.intensity - self.out.write('\t\t\t\n') - self.out.write('\t\t\t\t\n' - % (lamp.color.r*mult, lamp.color.g*mult, lamp.color.b*mult)) - self.out.write('\t\t\t\t\n' % lamp.samplingWeight) - self.out.write('\t\t\t\n') - self.out.write('\t\t\n') - self.out.write('\n') + self.openElement('luminaire', {'type' : 'area'}) + self.parameter('rgb', 'intensity', { 'value' : "%f %f %f" + % (lamp.color.r*mult, lamp.color.g*mult, lamp.color.b*mult)}) + self.closeElement() + self.closeElement() def exportCameraSettings(self, scene, camera): if scene.mitsuba_integrator.motionblur: @@ -450,10 +462,20 @@ class MtsExporter: shuttertime = scene.mitsuba_integrator.shuttertime shutterOpen = (scene.frame_current - shuttertime/2) * frameTime shutterClose = (scene.frame_current + shuttertime/2) * frameTime - self.out.write('\t\n' % translate_id(camera.name)) - self.out.write('\t\t\n' % shutterOpen) - self.out.write('\t\t\n' % shutterClose) - self.out.write('\t\n') + self.openElement('prepend', {'id' : '%s-camera' % translate_id(camera.name)}) + self.parameter('float', 'shutterOpen', {'value' : str(shutterOpen)}) + self.parameter('float', 'shutterClose', {'value' : str(shutterClose)}) + self.closeElement() + + def exportMedium(self, medium): + self.openElement('medium', {'id' : medium.name, 'type' : medium.type}) + if medium.g == 0: + self.element('phase', {'type' : 'isotropic'}) + else: + self.openElement('phase', {'type' : 'hg'}) + self.parameter('float', 'g', {'value' : str(medium.g)}) + self.closeElement() + self.closeElement() def export(self, scene): if scene.mitsuba_engine.binary_path == '': @@ -463,7 +485,6 @@ class MtsExporter: idx = 0 # Force scene update; NB, scene.update() doesn't work scene.frame_set(scene.frame_current) - efutil.export_path = self.xml_filename try: os.mkdir(self.meshes_dir) @@ -477,6 +498,8 @@ class MtsExporter: self.writeHeader() self.exportIntegrator(scene.mitsuba_integrator) self.exportSampler(scene.mitsuba_sampler) + for medium in scene.mitsuba_media.media: + self.exportMedium(medium) for obj in scene.objects: if obj.type == 'LAMP': self.exportLamp(obj, idx) @@ -490,7 +513,7 @@ class MtsExporter: idx = idx+1 self.writeFooter() (width, height) = resolution(scene) - + MtsLog("MtsBlend: Launching mtsimport") command = ['mtsimport', '-r', '%dx%d' % (width, height), '-n', '-l', 'pngfilm', self.dae_filename, self.xml_filename, self.adj_filename] diff --git a/include/mitsuba/core/sfcurve.h b/include/mitsuba/core/sfcurve.h index 4b46fcbe..bcbbf019 100644 --- a/include/mitsuba/core/sfcurve.h +++ b/include/mitsuba/core/sfcurve.h @@ -52,7 +52,7 @@ public: m_points.reserve(m_size.x*m_size.y); m_size = size; m_pos = PointType(0); generate( - log2i(std::max(m_size.x, m_size.y)), + log2i((uint32_t) std::max(m_size.x, m_size.y)), ENorth, EEast, ESouth, EWest); } diff --git a/include/mitsuba/core/util.h b/include/mitsuba/core/util.h index 34a51c31..e6634e27 100644 --- a/include/mitsuba/core/util.h +++ b/include/mitsuba/core/util.h @@ -104,17 +104,35 @@ extern MTS_EXPORT_CORE std::string formatString(const char *pFmt, ...); /// Base-2 logarithm extern MTS_EXPORT_CORE Float log2(Float value); -/// Base-2 logarithm (integer version) -extern MTS_EXPORT_CORE int log2i(int value); +/// Base-2 logarithm (32-bit integer version) +extern MTS_EXPORT_CORE int log2i(uint32_t value); + +/// Base-2 logarithm (64-bit integer version) +extern MTS_EXPORT_CORE int log2i(uint64_t value); /// Friendly modulo function (always positive) extern MTS_EXPORT_CORE int modulo(int a, int b); -/// Check if an integer is a power of two -extern MTS_EXPORT_CORE bool isPowerOfTwo(unsigned int i); +/// Check if an integer is a power of two (32 bit version) +inline bool isPowerOfTwo(uint32_t i) { + return (i & (i-1)) == 0; +} + +/// Check if an integer is a power of two (signed 32 bit version) +inline bool isPowerOfTwo(int32_t i) { + return i > 0 && (i & (i-1)) == 0; +} + +/// Check if an integer is a power of two (64 bit version) +inline bool isPowerOfTwo(uint64_t i) { + return (i & (i-1)) == 0; +} /// Round an integer to the next power of two -extern MTS_EXPORT_CORE unsigned int roundToPowerOfTwo(unsigned int i); +extern MTS_EXPORT_CORE uint32_t roundToPowerOfTwo(uint32_t i); + +/// Round an integer to the next power of two (64 bit version) +extern MTS_EXPORT_CORE uint64_t roundToPowerOfTwo(uint64_t i); //// Windowed sinc filter (Lanczos envelope, tau=number of cycles) extern MTS_EXPORT_CORE Float lanczosSinc(Float t, Float tau = 2); diff --git a/include/mitsuba/render/gkdtree.h b/include/mitsuba/render/gkdtree.h index 69294846..2f477d65 100644 --- a/include/mitsuba/render/gkdtree.h +++ b/include/mitsuba/render/gkdtree.h @@ -923,7 +923,7 @@ protected: KDLog(EError, "The exact primitive threshold must be >= 0"); if (m_minMaxBins <= 1) KDLog(EError, "The number of min-max bins must be > 2"); - + size_type primCount = cast()->getPrimitiveCount(); if (primCount == 0) { KDLog(EWarn, "kd-tree contains no geometry!"); @@ -933,6 +933,9 @@ protected: return; } + if (primCount <= m_exactPrimThreshold) + m_parallelBuild = false; + BuildContext ctx(primCount, m_minMaxBins); /* Establish an ad-hoc depth cutoff value (Formula from PBRT) */ diff --git a/include/mitsuba/render/skdtree.h b/include/mitsuba/render/skdtree.h index 11f5e614..171d9c80 100644 --- a/include/mitsuba/render/skdtree.h +++ b/include/mitsuba/render/skdtree.h @@ -389,6 +389,7 @@ protected: if (EXPECT_TAKEN(!vertexTangents)) { its.shFrame = Frame(normalize(n0 * b.x + n1 * b.y + n2 * b.z)); + its.dpdu = its.dpdv = Vector(0.0f); } else { const TangentSpace &t0 = vertexTangents[idx0]; const TangentSpace &t1 = vertexTangents[idx1]; @@ -403,6 +404,7 @@ protected: } } else { its.shFrame = its.geoFrame; + its.dpdu = its.dpdv = Vector(0.0f); } if (EXPECT_TAKEN(vertexTexcoords)) { diff --git a/src/bsdfs/SConscript b/src/bsdfs/SConscript index 42f467a1..80979651 100644 --- a/src/bsdfs/SConscript +++ b/src/bsdfs/SConscript @@ -12,5 +12,6 @@ plugins += env.SharedLibrary('#plugins/microfacet', ['microfacet.cpp']) plugins += env.SharedLibrary('#plugins/roughglass', ['roughglass.cpp']) plugins += env.SharedLibrary('#plugins/roughmetal', ['roughmetal.cpp']) plugins += env.SharedLibrary('#plugins/composite', ['composite.cpp']) +plugins += env.SharedLibrary('#plugins/twosided', ['twosided.cpp']) Export('plugins') diff --git a/src/bsdfs/twosided.cpp b/src/bsdfs/twosided.cpp new file mode 100644 index 00000000..e8d9bab4 --- /dev/null +++ b/src/bsdfs/twosided.cpp @@ -0,0 +1,193 @@ +/* + This file is part of Mitsuba, a physically based rendering system. + + Copyright (c) 2007-2010 by Wenzel Jakob and others. + + Mitsuba is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License Version 3 + as published by the Free Software Foundation. + + Mitsuba 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, see . +*/ + +#include +#include +#include + +MTS_NAMESPACE_BEGIN + +class TwoSidedBRDF : public BSDF { +public: + TwoSidedBRDF(const Properties &props) + : BSDF(props) { } + + TwoSidedBRDF(Stream *stream, InstanceManager *manager) + : BSDF(stream, manager) { + m_nestedBRDF = static_cast(manager->getInstance(stream)); + configure(); + } + + virtual ~TwoSidedBRDF() { + if (m_type) + delete m_type; + } + + void serialize(Stream *stream, InstanceManager *manager) const { + BSDF::serialize(stream, manager); + + manager->serialize(stream, m_nestedBRDF.get()); + } + + void configure() { + if (!m_nestedBRDF) + Log(EError, "TwoSidedBRDF: A child BRDF instance is required"); + m_combinedType = m_nestedBRDF->getType(); + if (m_combinedType & BSDF::ETransmission) + Log(EError, "TwoSidedBRDF: only BRDF child instances (without " + "transmission) are supported"); + m_usesRayDifferentials = m_nestedBRDF->usesRayDifferentials(); + m_componentCount = m_nestedBRDF->getComponentCount(); + m_type = new unsigned int[m_componentCount]; + for (int i=0; igetComponentCount(); ++i) + m_type[i] = m_nestedBRDF->getType(i); + } + + Spectrum getDiffuseReflectance(const Intersection &its) const { + return m_nestedBRDF->getDiffuseReflectance(its); + } + + Spectrum f(const BSDFQueryRecord &bRec) const { + BSDFQueryRecord b(bRec); + if (b.wi.z < 0) { + b.wi.z *= -1; + b.wo.z *= -1; + } + return m_nestedBRDF->f(b); + } + + + Float pdf(const BSDFQueryRecord &bRec) const { + BSDFQueryRecord b(bRec); + if (b.wi.z < 0) { + b.wi.z *= -1; + b.wo.z *= -1; + } + return m_nestedBRDF->pdf(b); + } + + + Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const { + bool flip = false; + if (bRec.wi.z < 0) { + bRec.wi.z *= -1; + flip = true; + } + Spectrum result = m_nestedBRDF->sample(bRec, sample); + if (bRec.wi.z < 0 && !result.isZero()) { + bRec.wi.z *= -1; + bRec.wo.z *= -1; + flip = true; + } + return result; + } + + Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &sample) const { + bool flip = false; + if (bRec.wi.z < 0) { + bRec.wi.z *= -1; + flip = true; + } + Spectrum result = m_nestedBRDF->sample(bRec, pdf, sample); + if (bRec.wi.z < 0 && !result.isZero()) { + bRec.wi.z *= -1; + bRec.wo.z *= -1; + flip = true; + } + return result; + } + + void addChild(const std::string &name, ConfigurableObject *child) { + if (child->getClass()->derivesFrom(BSDF::m_theClass)) { + m_nestedBRDF = static_cast(child); + } else { + BSDF::addChild(name, child); + } + } + + std::string toString() const { + std::ostringstream oss; + oss << "TwoSided[" << endl + << " nestedBRDF = " << indent(m_nestedBRDF->toString()) << endl + << "]"; + return oss.str(); + } + + + Shader *createShader(Renderer *renderer) const; + + MTS_DECLARE_CLASS() +protected: + ref m_nestedBRDF; +}; + + +// ================ Hardware shader implementation ================ + +class TwoSidedShader : public Shader { +public: + TwoSidedShader(Renderer *renderer, + const BSDF *nestedBRDF) : Shader(renderer, EBSDFShader), + m_nestedBRDF(nestedBRDF) { + m_nestedBRDFShader = renderer->registerShaderForResource(nestedBRDF); + } + + bool isComplete() const { + return m_nestedBRDFShader.get() != NULL; + } + + void putDependencies(std::vector &deps) { + deps.push_back(m_nestedBRDFShader.get()); + } + + void cleanup(Renderer *renderer) { + renderer->unregisterShaderForResource(m_nestedBRDF); + } + + void generateCode(std::ostringstream &oss, + const std::string &evalName, + const std::vector &depNames) const { + oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl + << " if (wi.z <= 0.0) {" << endl + << " wi.z *= -1; wo.z *= -1;" << endl + << " }" << endl + << " return " << depNames[0] << "(uv, wi, wo);" << endl + << "}" << endl + << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl + << " if (wi.z <= 0.0) {" << endl + << " wi.z *= -1; wo.z *= -1;" << endl + << " }" << endl + << " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl + << "}" << endl; + } + + MTS_DECLARE_CLASS() +private: + const BSDF *m_nestedBRDF; + ref m_nestedBRDFShader; + bool m_complete; +}; + +Shader *TwoSidedBRDF::createShader(Renderer *renderer) const { + return new TwoSidedShader(renderer, m_nestedBRDF.get()); +} + +MTS_IMPLEMENT_CLASS(TwoSidedShader, false, Shader) +MTS_IMPLEMENT_CLASS_S(TwoSidedBRDF, false, BSDF) +MTS_EXPORT_PLUGIN(TwoSidedBRDF, "Two-sided BRDF adapter"); +MTS_NAMESPACE_END diff --git a/src/converter/collada.cpp b/src/converter/collada.cpp index 58da730c..6cf3b5d9 100644 --- a/src/converter/collada.cpp +++ b/src/converter/collada.cpp @@ -224,6 +224,7 @@ VertexData *fetchVertexData(Transform transform, result->typeToOffset[EUV] = offset; result->typeToOffsetInStream[EUV] = offsetInStream; result->typeToCount[EUV] = size; + cout << "Got texture coordinates.." << endl; } else { SLog(EWarn, "Found multiple sets of texture coordinates - ignoring!"); } diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index 27a2550b..d3091795 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -244,7 +244,7 @@ bool enableFPExceptions() { bool exceptionsWereEnabled = false; #if defined(WIN32) _clearfp(); - unsigned int cw = _controlfp(0, 0); + uint32_t cw = _controlfp(0, 0); exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); cw &= ~(_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); _controlfp(cw, _MCW_EM); @@ -268,7 +268,7 @@ bool disableFPExceptions() { bool exceptionsWereEnabled = false; #if defined(WIN32) _clearfp(); - unsigned int cw = _controlfp(0, 0); + uint32_t cw = _controlfp(0, 0); exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); cw |= _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW; _controlfp(cw, _MCW_EM); @@ -288,7 +288,7 @@ bool disableFPExceptions() { void restoreFPExceptions(bool oldState) { bool currentState; #if defined(WIN32) - unsigned int cw = _controlfp(0, 0); + uint32_t cw = _controlfp(0, 0); currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); #elif defined(__OSX__) currentState = query_fpexcept_sse() != 0; @@ -381,11 +381,18 @@ std::string formatString(const char *fmt, ...) { return std::string(tmp); } -int log2i(int value) { - int r = 0; - while ((value >> r) != 0) - r++; - return r-1; +int log2i(uint32_t value) { + int r = 0; + while ((value >> r) != 0) + r++; + return r-1; +} + +int log2i(uint64_t value) { + int r = 0; + while ((value >> r) != 0) + r++; + return r-1; } int modulo(int a, int b) { @@ -394,11 +401,7 @@ int modulo(int a, int b) { } /* Fast rounding & power-of-two test algorithms from PBRT */ -bool isPowerOfTwo(unsigned int i) { - return (i & (i-1)) == 0; -} - -unsigned int roundToPowerOfTwo(unsigned int i) { +uint32_t roundToPowerOfTwo(uint32_t i) { i--; i |= i >> 1; i |= i >> 2; i |= i >> 4; i |= i >> 8; @@ -406,6 +409,13 @@ unsigned int roundToPowerOfTwo(unsigned int i) { return i+1; } +uint64_t roundToPowerOfTwo(uint64_t i) { + i--; + i |= i >> 1; i |= i >> 2; + i |= i >> 4; i |= i >> 8; + i |= i >> 16; i |= i >> 32; + return i+1; +} // ----------------------------------------------------------------------- // Numerical utility functions diff --git a/src/librender/mipmap.cpp b/src/librender/mipmap.cpp index f1d03f93..ac2b5faf 100644 --- a/src/librender/mipmap.cpp +++ b/src/librender/mipmap.cpp @@ -32,8 +32,8 @@ MIPMap::MIPMap(int width, int height, Spectrum *pixels, Spectrum *texture = pixels; if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) { - m_width = roundToPowerOfTwo(width); - m_height = roundToPowerOfTwo(height); + m_width = (int) roundToPowerOfTwo((uint32_t) width); + m_height = (int) roundToPowerOfTwo((uint32_t) height); /* The texture needs to be up-sampled */ Spectrum *texture1 = new Spectrum[m_width*height]; @@ -87,7 +87,7 @@ MIPMap::MIPMap(int width, int height, Spectrum *pixels, delete[] texture1; } - m_levels = 1 + log2i(std::max(width, height)); + m_levels = 1 + log2i((uint32_t) std::max(width, height)); m_pyramid = new Spectrum*[m_levels]; m_pyramid[0] = texture; m_levelWidth = new int[m_levels]; diff --git a/src/librender/trimesh.cpp b/src/librender/trimesh.cpp index a338e8d9..33625e8d 100644 --- a/src/librender/trimesh.cpp +++ b/src/librender/trimesh.cpp @@ -285,7 +285,7 @@ void TriMesh::configure() { } if (hasBSDF() && ((m_bsdf->getType() & BSDF::EAnisotropicMaterial) - || m_bsdf->usesRayDifferentials()) && !m_tangents) + || m_bsdf->usesRayDifferentials()) && !m_tangents) computeTangentSpaceBasis(); } @@ -546,10 +546,12 @@ void TriMesh::computeNormals() { bool TriMesh::computeTangentSpaceBasis() { int zeroArea = 0, zeroNormals = 0; if (!m_texcoords) { - if (hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropicMaterial) - Log(EError, "\"%s\": computeTangentSpace(): texture coordinates are required " - "to generate tangent vectors. If you want to render with an anisotropic " - "material, make sure that all assigned objects have texture coordinates."); + bool anisotropic = hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropicMaterial; + if (anisotropic) + Log(EError, "\"%s\": computeTangentSpace(): texture coordinates " + "are required to generate tangent vectors. If you want to render with an anisotropic " + "material, please make sure that all associated shapes have valid texture coordinates.", + getName().c_str()); return false; } diff --git a/src/qtgui/mainwindow.ui b/src/qtgui/mainwindow.ui index a827617f..273bf985 100644 --- a/src/qtgui/mainwindow.ui +++ b/src/qtgui/mainwindow.ui @@ -470,7 +470,7 @@ - Describe Scene.. + Scene Information .. diff --git a/src/qtgui/rendersettingsdlg.cpp b/src/qtgui/rendersettingsdlg.cpp index 4a2e2d9d..8b3a38fd 100644 --- a/src/qtgui/rendersettingsdlg.cpp +++ b/src/qtgui/rendersettingsdlg.cpp @@ -183,17 +183,10 @@ void RenderSettingsDialog::onTreeSelectionChange(const QItemSelection &selected, void RenderSettingsDialog::update() { int index = ui->integratorBox->currentIndex(); - Properties integratorProps; + Properties integratorProps, samplerProps; - int sampleCount = -1; - if (sender() == ui->samplerBox) { - Properties samplerProps; + if (sender() == ui->samplerBox) m_samplerNode->putProperties(samplerProps); - if (samplerProps.hasProperty("sampleCount")) - sampleCount = samplerProps.getInteger("sampleCount"); - else if (samplerProps.hasProperty("resolution")) - sampleCount = (int) std::pow((Float) samplerProps.getInteger("resolution"), (Float) 2); - } if (sender() == ui->integratorBox) m_integratorNode->putProperties(integratorProps); @@ -224,18 +217,6 @@ void RenderSettingsDialog::update() { m_aiNode = m_model->updateClass(m_aiNode, "", ""); } - if (sender() == ui->samplerBox && sampleCount != -1) { - std::string samplerPlugin = getPluginName(ui->samplerBox); - for (int i=0; ichildCount(); ++i) { - TreeItem *treeItem = m_samplerNode->child(i); - if (treeItem->getName() == "sampleCount") { - treeItem->setValue(sampleCount); - } else if (treeItem->getName() == "resolution") { - treeItem->setValue((int) std::sqrt((Float) sampleCount)); - } - } - } - if (sender() == ui->integratorBox) { for (int i=0; ichildCount(); ++i) { TreeItem *treeItem = m_integratorNode->child(i); @@ -243,6 +224,14 @@ void RenderSettingsDialog::update() { m_integratorNode->setProperty(treeItem->getName().toStdString(), integratorProps); } } + + if (sender() == ui->samplerBox) { + for (int i=0; ichildCount(); ++i) { + TreeItem *treeItem = m_samplerNode->child(i); + if (samplerProps.hasProperty(treeItem->getName().toStdString())) + m_samplerNode->setProperty(treeItem->getName().toStdString(), samplerProps); + } + } ui->treeView->expandAll(); dataChanged(); diff --git a/src/qtgui/resources/docs.xml b/src/qtgui/resources/docs.xml index 2673ae37..d503b2fc 100644 --- a/src/qtgui/resources/docs.xml +++ b/src/qtgui/resources/docs.xml @@ -1044,19 +1044,16 @@ - Independent sample generator - returns independent uniformly distributed random numbers on [0,1) and [0, 1)x[0, 1). - Number of generated samples / samples per pixel. + Independent sample generator - computes independent uniformly distributed random numbers without any kind of stratification. + Number of samples per pixel - - Stratified sample generator. Given a resolution R and a depth D, it - generates R*R samples, each of which can be queried for up to D 1- - or 2-dimensional vectors by an integrator. The returned 1D/2D-vectors of a particular depth - have the property of being stratified over all R*R samples. When - the maximum depth is exceeded, this implementation reverts to independent sampling. + Stratified sample generator - computes stratified samples in 1 and 2 dimensions. + This only works up to a specified maximum depth, after which + the implementation falls back to independent sampling. - Stratification resolution of the sample space. The default results in 2x2=4 samples per pixel + Number of samples per pixel (will be rounded up to the next perfect square) Depth, up to which which stratified samples are guaranteed to be available. @@ -1066,7 +1063,7 @@ Provides samples up to a specified depth, after which independent sampling takes over. - Number of generated samples / samples per pixel + Number of samples per pixel (will be rounded up to the next power of two) Depth, up to which which low discrepancy samples are guaranteed to be available. diff --git a/src/qtgui/sceneinfodlg.ui b/src/qtgui/sceneinfodlg.ui index 7b43bbac..f94de6a3 100644 --- a/src/qtgui/sceneinfodlg.ui +++ b/src/qtgui/sceneinfodlg.ui @@ -49,6 +49,9 @@ 8 + + QTextEdit::NoWrap + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> diff --git a/src/samplers/independent.cpp b/src/samplers/independent.cpp index d5345627..710e5694 100644 --- a/src/samplers/independent.cpp +++ b/src/samplers/independent.cpp @@ -31,7 +31,7 @@ public: IndependentSampler(const Properties &props) : Sampler(props) { /* Number of samples per pixel when used with a sampling-based integrator */ - m_sampleCount = props.getSize("sampleCount", 1); + m_sampleCount = props.getSize("sampleCount", 4); m_random = new Random(); } diff --git a/src/samplers/ldsampler.cpp b/src/samplers/ldsampler.cpp index 7837d40e..2fbc495f 100644 --- a/src/samplers/ldsampler.cpp +++ b/src/samplers/ldsampler.cpp @@ -27,8 +27,7 @@ MTS_NAMESPACE_BEGIN */ class LowDiscrepancySampler : public Sampler { public: - LowDiscrepancySampler() : Sampler(Properties()) { - } + LowDiscrepancySampler() : Sampler(Properties()) { } LowDiscrepancySampler(Stream *stream, InstanceManager *manager) : Sampler(stream, manager) { @@ -44,23 +43,24 @@ public: } LowDiscrepancySampler(const Properties &props) : Sampler(props) { - /* Number of samples per pixel when used with a sampling-based integrator */ + /* Sample count (will be rounded up to the next power of two) */ m_sampleCount = props.getSize("sampleCount", 4); /* Depth, up to which which low discrepancy samples are guaranteed to be available. */ m_depth = props.getInteger("depth", 3); - if (!isPowerOfTwo((int) m_sampleCount)) { - m_sampleCount = roundToPowerOfTwo((int) m_sampleCount); - Log(EWarn, "Sample count should be a power of two - rounding to %i", m_sampleCount); + if (!isPowerOfTwo((uint64_t) m_sampleCount)) { + m_sampleCount = (size_t) roundToPowerOfTwo((uint64_t) m_sampleCount); + Log(EWarn, "Sample count should be a power of two -- rounding to " + SIZE_T_FMT, m_sampleCount); } m_samples1D = new Float*[m_depth]; m_samples2D = new Point2*[m_depth]; for (int i=0; im_samples1D = new Float*[m_depth]; sampler->m_samples2D = new Point2*[m_depth]; for (int i=0; im_samples1D[i] = new Float[(size_t) m_sampleCount]; - sampler->m_samples2D[i] = new Point2[(size_t) m_sampleCount]; + sampler->m_samples1D[i] = new Float[m_sampleCount]; + sampler->m_samples2D[i] = new Point2[m_sampleCount]; } for (size_t i=0; irequest2DArray(m_req1D[i]); diff --git a/src/samplers/stratified.cpp b/src/samplers/stratified.cpp index f830187e..3bb9e081 100644 --- a/src/samplers/stratified.cpp +++ b/src/samplers/stratified.cpp @@ -34,20 +34,31 @@ public: } StratifiedSampler(const Properties &props) : Sampler(props) { - /* Stratification resolution of the sample space. Default: 2, - resulting in 2x2=4 samples per pixel */ - m_resolution = props.getInteger("resolution", 2); + /* Sample count (will be rounded up to the next perfect square) */ + size_t desiredSampleCount = props.getSize("sampleCount", 4); + + size_t i = 1; + while (i * i < desiredSampleCount) + ++i; + m_sampleCount = i*i; + + if (m_sampleCount != desiredSampleCount) { + Log(EWarn, "Sample count should be a perfect square -- rounding to " + SIZE_T_FMT, m_sampleCount); + } + + m_resolution = (int) i; /* Depth, up to which which stratified samples are guaranteed to be available. */ m_depth = props.getInteger("depth", 3); m_sampleCount = m_resolution*m_resolution; - m_permutations1D = new unsigned int*[m_depth]; - m_permutations2D = new unsigned int*[m_depth]; + m_permutations1D = new uint32_t*[m_depth]; + m_permutations2D = new uint32_t*[m_depth]; for (int i=0; ireadInt(); m_resolution = stream->readInt(); m_random = static_cast(manager->getInstance(stream)); - m_permutations1D = new unsigned int*[m_depth]; - m_permutations2D = new unsigned int*[m_depth]; + m_permutations1D = new uint32_t*[m_depth]; + m_permutations2D = new uint32_t*[m_depth]; for (int i=0; im_invResolutionSquare = m_invResolutionSquare; sampler->m_random = new Random(m_random); - sampler->m_permutations1D = new unsigned int*[m_depth]; - sampler->m_permutations2D = new unsigned int*[m_depth]; + sampler->m_permutations1D = new uint32_t*[m_depth]; + sampler->m_permutations2D = new uint32_t*[m_depth]; for (int i=0; im_permutations1D[i] = new unsigned int[m_sampleCount]; - sampler->m_permutations2D[i] = new unsigned int[m_sampleCount]; + sampler->m_permutations1D[i] = new uint32_t[m_sampleCount]; + sampler->m_permutations2D[i] = new uint32_t[m_sampleCount]; } for (size_t i=0; irequest2DArray(m_req1D[i]); @@ -112,11 +123,11 @@ public: void generate() { for (int i=0; ishuffle(&m_permutations1D[i][0], &m_permutations1D[i][m_sampleCount]); for (size_t j=0; jshuffle(&m_permutations2D[i][0], &m_permutations2D[i][m_sampleCount]); } @@ -200,7 +211,7 @@ private: int m_resolution; int m_depth; Float m_invResolution, m_invResolutionSquare; - unsigned int **m_permutations1D, **m_permutations2D; + uint32_t **m_permutations1D, **m_permutations2D; int m_sampleDepth1D, m_sampleDepth2D; }; diff --git a/src/shapes/serialized.cpp b/src/shapes/serialized.cpp index 206b50f5..f3ad8eba 100644 --- a/src/shapes/serialized.cpp +++ b/src/shapes/serialized.cpp @@ -38,7 +38,8 @@ public: /// When the file contains multiple meshes, this index specifies which one to load int shapeIndex = props.getInteger("shapeIndex", 0); - m_name = (props.getID() != "unnamed") ? props.getID() : formatString("%s@%i", filePath.stem().c_str(), shapeIndex); + m_name = (props.getID() != "unnamed") ? props.getID() + : formatString("%s@%i", filePath.stem().c_str(), shapeIndex); /* Load the geometry */ Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.leaf().c_str());