diff --git a/SConstruct b/SConstruct index c881913a..2538ea12 100644 --- a/SConstruct +++ b/SConstruct @@ -20,7 +20,7 @@ if sys.platform == 'win32': # Set an application icon on Windows resources += [ env.RES('data/windows/mitsuba_res.rc') ] # Convert the command line args from UTF-8 to UTF-16 - winstubs += [os.path.abspath('data/windows/wmain_stub.cpp')] + winstubs += [ env.SharedObject('#data/windows/wmain_stub.cpp') ] Export('winstubs') def build(scriptFile, exports = [], duplicate = 0): diff --git a/build/SConscript.configure b/build/SConscript.configure index 3a65f817..73d917be 100644 --- a/build/SConscript.configure +++ b/build/SConscript.configure @@ -285,7 +285,7 @@ if needsBuildDependencies: print '\nThe dependency directory and your Mitsuba codebase have different version' print 'numbers! Your copy of Mitsuba has version %s, whereas the dependencies ' % MTS_VERSION print 'have version %s. Please bring them into sync, either by running\n' % depVersion - print '$ hg update -r v%s\n' % depVersion + print '$ hg update -r v%s\n' % depVersion print 'in the Mitsuba directory, or by running\n' print '$ cd dependencies' print '$ hg pull' @@ -353,7 +353,7 @@ def configure_for_objective_cpp(env): env.RemoveFlags(['-fstrict-aliasing', '-ftree-vectorize', '-std=c\+\+0x']) # Remove Intel compiler-specific optimization flags - env.RemoveFlags(['-x.*', '-ax.*', '-ipo', '-no-prec-div', + env.RemoveFlags(['-x.*', '-ax.*', '-ipo', '-no-prec-div', '-fp-model', 'fast=.*', '-wd.*', '-openmp']) env['CCFLAGS'] += ['-fno-strict-aliasing'] # Enforce GCC usage (Intel compiler doesn't handle Objective C/C++) @@ -374,7 +374,7 @@ env.__class__.ConfigureForObjectiveCPP = configure_for_objective_cpp env.__class__.RelaxCompilerSettings = relax_compiler_settings if hasCollada: - env.Append(CPPDEFINES = [['MTS_HAS_COLLADA', 1]] ) + env.Append(CPPDEFINES = [['MTS_HAS_COLLADA', 1]]) env.SConsignFile() diff --git a/data/linux/debian/control b/data/linux/debian/control index 8bf000ec..ea4c13b7 100644 --- a/data/linux/debian/control +++ b/data/linux/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Wenzel Jakob Build-Depends: debhelper (>= 7), build-essential, scons, qt4-dev-tools, libpng12-dev, libjpeg-dev, libilmbase-dev, libopenexr-dev, - libxerces-c-dev, libboost-dev, libglewmx1.5-dev, libxxf86vm-dev, + libxerces-c-dev, libboost-dev, libglewmx-dev, libxxf86vm-dev, collada-dom-dev, libboost-system-dev, libboost-filesystem-dev, libboost-python-dev, libboost-thread-dev, libgl1-mesa-dev, libglu1-mesa-dev, pkg-config, libeigen3-dev @@ -24,7 +24,7 @@ Description: Mitsuba renderer Package: mitsuba-dev Architecture: any Depends: qt4-dev-tools, libpng12-dev, libjpeg-dev, libilmbase-dev, - libopenexr-dev, libxerces-c-dev, libboost-dev, libglewmx1.5-dev, + libopenexr-dev, libxerces-c-dev, libboost-dev, libglewmx-dev, libxxf86vm-dev, collada-dom-dev, libboost-system-dev, libboost-filesystem-dev, libboost-python-dev, libboost-thread-dev, libeigen3-dev, mitsuba diff --git a/data/pch/mitsuba_precompiled.hpp b/data/pch/mitsuba_precompiled.hpp index cbe1e530..9d31a26d 100644 --- a/data/pch/mitsuba_precompiled.hpp +++ b/data/pch/mitsuba_precompiled.hpp @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/doc/section_subsurface.tex b/doc/section_subsurface.tex index 40fff2cb..5c446a91 100644 --- a/doc/section_subsurface.tex +++ b/doc/section_subsurface.tex @@ -2,11 +2,18 @@ \subsection{Subsurface scattering models} \label{sec:subsurface} There are two ways of simulating subsurface scattering within Mitsuba: -participating media and subsurface scattering models. The latter are described -in this section and can be thought of as a first-order approximation of the -former. For this reason, subsurface scattering models should be preferred when -visually appealing output should be generated quickly and the demands on -physical realism are secondary. +participating media and subsurface scattering models. + +\begin{description} + \item[Subsurface scattering models:] Described in this section. These can be thought + of as a first-order approximation of what happens inside a participating medium. + They are preferable when visually appealing output should be generated + \emph{quickly} and the demands on accuracy are secondary. + At the moment, there is only one subsurface scattering model (the + \pluginref{dipole}), which is described on the next page. + \item[Participating media:] Described in Section~\ref{sec:media}. When modeling + subsurface scattering using a participating medium, Mitsuba performs a \emph{full} + radiative transport simulation, which correctly accounts for all scattering events. + This is more accurate but generally significantly slower. +\end{description} -At the moment, there is only one subsurface scattering model (the -\pluginref{dipole}), which is described on the next page. diff --git a/include/mitsuba/core/bitmap.h b/include/mitsuba/core/bitmap.h index d7f58d73..b05a6197 100644 --- a/include/mitsuba/core/bitmap.h +++ b/include/mitsuba/core/bitmap.h @@ -393,17 +393,8 @@ public: /// Draw a rectangle with the specified position and size void drawRect(const Point2i &offset, const Vector2i &size, const Spectrum &value); - /** - * \brief Color balancing: apply the given scale factors to the - * red, green, and blue channels of the image - * - * When the image is not an \c EFloat16, \c EFloat32, or - * \c EFloat64-based RGB/RGBA image, the function throws an exception - */ - void colorBalance(Float r, Float g, Float b); - /// Draw a filled rectangle with the specified position and size - void fill(const Point2i &offset, const Vector2i &size, const Spectrum &value); + void fillRect(Point2i offset, Vector2i size, const Spectrum &value); /// Bitmap equality operator (useful for unit-tests etc.) bool operator==(const Bitmap &bitmap) const; @@ -699,7 +690,43 @@ public: * use different component formats or channels, or when the * component format is \ref EBitmask. */ - void accumulate(const Bitmap *bitmap, const Point2i &offset); + void accumulate(const Bitmap *bitmap, Point2i sourceOffset, + Point2i targetOffset, Vector2i size); + + /** + * \brief Color balancing: apply the given scale factors to the + * red, green, and blue channels of the image + * + * When the image is not an \c EFloat16, \c EFloat32, or + * \c EFloat64-based RGB/RGBA image, the function throws an exception + */ + void colorBalance(Float r, Float g, Float b); + + /** + * Apply a color transformation matrix to the contents of the bitmap + * + * The implementation assumes that the contents have the + * RGB, RGBA, XYZ, or XYZA pixel format and a floating point + * component format. + */ + void applyMatrix(Float matrix[3][3]); + + /** + * \brief Accumulate the contents of another bitmap into the + * region of the specified offset + * + * This convenience function calls the main accumulate() + * implementation with size set to bitmap->getSize() + * and sourceOffset set to zero. Out-of-bounds regions are + * ignored. It is assumed that bitmap != this. + * + * \remark This function throws an exception when the bitmaps + * use different component formats or channels, or when the + * component format is \ref EBitmask. + */ + inline void accumulate(const Bitmap *bitmap, Point2i targetOffset) { + accumulate(bitmap, Point2i(0), targetOffset, bitmap->getSize()); + } /** * \brief Up- or down-sample this image to a different resolution diff --git a/include/mitsuba/core/platform.h b/include/mitsuba/core/platform.h index 00c9791a..e949545e 100644 --- a/include/mitsuba/core/platform.h +++ b/include/mitsuba/core/platform.h @@ -189,6 +189,55 @@ extern MTS_EXPORT_CORE void __mts_set_appdefaults(); #define MTS_AUTORELEASE_BEGIN() #define MTS_AUTORELEASE_END() #endif + MTS_NAMESPACE_END + +/// \cond +// Try to make MSVC++ behave a bit more like C++ +// with an underlying C99 implementation +// (and dn't include this in the documentation) +#if defined(_MSC_VER) + +#include + +#define snprintf _snprintf +#define vsnprintf _vsnprintf + +namespace std { + + inline char tolower(char c) { + return ::tolower(c); + } + + inline char toupper(char c) { + return ::toupper(c); + } + + inline bool isnan(float f) { + return _isnan(f); + } + + inline bool isnan(double f) { + return _isnan(f); + } + + inline bool isfinite(float f) { + return _finite(f); + } + + inline bool isfinite(double f) { + return _finite(f); + } + + inline bool isinf(float f) { + return !_finite(f); + } + + inline bool isinf(double f) { + return !_finite(f); + } +}; +#endif + #endif /* __MITSUBA_CORE_PLATFORM_H_ */ diff --git a/include/mitsuba/core/spectrum.h b/include/mitsuba/core/spectrum.h index eb02ba45..9d1095d3 100644 --- a/include/mitsuba/core/spectrum.h +++ b/include/mitsuba/core/spectrum.h @@ -493,6 +493,14 @@ public: return value; } + /// Component-wise square root + inline TSpectrum safe_sqrt() const { + TSpectrum value; + for (int i=0; i -using __gnu_cxx::select2nd; -using __gnu_cxx::compose1; -#else -#include - -/// \cond -// (Don't include in the documentation) -namespace std { - template struct _Select1st : public unary_function<_Pair, typename _Pair::first_type> { - const typename _Pair::first_type& operator()(const _Pair& __x) const { - return __x.first; - } - }; - - template struct _Select2nd : public unary_function<_Pair, typename _Pair::second_type> { - const typename _Pair::second_type& operator()(const _Pair& __x) const { - return __x.second; - } - }; - - template struct select1st : public _Select1st<_Pair> {}; - template struct select2nd : public _Select2nd<_Pair> {}; - - template class unary_compose : public unary_function { - protected: - _Operation1 _M_fn1; - _Operation2 _M_fn2; - public: - unary_compose(const _Operation1& __x, const _Operation2& __y) : _M_fn1(__x), _M_fn2(__y) {} - typename _Operation1::result_type operator()(const typename _Operation2::argument_type& __x) const { - return _M_fn1(_M_fn2(__x)); - } - }; - - template inline unary_compose<_Operation1,_Operation2> compose1(const _Operation1& __fn1, const _Operation2& __fn2) { - return unary_compose<_Operation1,_Operation2>(__fn1, __fn2); - } - -#if defined(_MSC_VER) - #include - - #define snprintf _snprintf - #define vsnprintf _vsnprintf - - inline char tolower(char c) { - return ::tolower(c); - } - - inline char toupper(char c) { - return ::toupper(c); - } - - inline bool isnan(float f) { - return _isnan(f); - } - - inline bool isnan(double f) { - return _isnan(f); - } - - inline bool isfinite(float f) { - return _finite(f); - } - - inline bool isfinite(double f) { - return _finite(f); - } - - inline bool isinf(float f) { - return !_finite(f); - } - - inline bool isinf(double f) { - return !_finite(f); - } -#endif -}; -using std::select2nd; -using std::compose1; -#endif - namespace mitsuba { namespace math { #if defined(__LINUX__) && defined(__x86_64__) diff --git a/include/mitsuba/core/util.h b/include/mitsuba/core/util.h index 2a02fd2e..985afeef 100644 --- a/include/mitsuba/core/util.h +++ b/include/mitsuba/core/util.h @@ -507,22 +507,99 @@ inline Float fresnelDielectricExt(Float cosThetaI, Float eta) { Float cosThetaT; return fresnelDielectricExt(cosThetaI, cosThetaT, eta); } /** - * \brief Calculates the unpolarized fresnel reflection coefficient - * at a planar interface between vacuum and a conductor. + * \brief Calculates the unpolarized Fresnel reflection coefficient + * at a planar interface having a complex-valued relative index of + * refraction (approximate scalar version) + * + * The implementation of this function relies on a simplified expression + * that becomes increasingly accurate as k grows. + * + * The name of this function is a slight misnomer, since it supports + * the general case of a complex-valued relative index of refraction + * (rather than being restricted to conductors) * * \param cosThetaI * Cosine of the angle between the normal and the incident ray * \param eta - * Real refractive index (wavelength-dependent) + * Relative refractive index (real component) * \param k - * Imaginary refractive index (wavelength-dependent) + * Relative refractive index (imaginary component) * \ingroup libpython */ -extern MTS_EXPORT_CORE Spectrum fresnelConductor(Float cosThetaI, +extern MTS_EXPORT_CORE Float fresnelConductorApprox(Float cosThetaI, + Float eta, Float k); + +/** + * \brief Calculates the unpolarized Fresnel reflection coefficient + * at a planar interface having a complex-valued relative index of + * refraction (approximate vectorized version) + * + * The implementation of this function relies on a simplified expression + * that becomes increasingly accurate as k grows. + * + * The name of this function is a slight misnomer, since it supports + * the general case of a complex-valued relative index of refraction + * (rather than being restricted to conductors) + * + * \param cosThetaI + * Cosine of the angle between the normal and the incident ray + * \param eta + * Relative refractive index (real component) + * \param k + * Relative refractive index (imaginary component) + * \ingroup libpython + */ +extern MTS_EXPORT_CORE Spectrum fresnelConductorApprox(Float cosThetaI, const Spectrum &eta, const Spectrum &k); /** - * \brief Calculates the diffuse unpolarized fresnel reflectance of + * \brief Calculates the unpolarized Fresnel reflection coefficient + * at a planar interface having a complex-valued relative index of + * refraction (accurate scalar version) + * + * The implementation of this function computes the exact unpolarized + * Fresnel reflectance for a complex index of refraction change. + * + * The name of this function is a slight misnomer, since it supports + * the general case of a complex-valued relative index of refraction + * (rather than being restricted to conductors) + * + * \param cosThetaI + * Cosine of the angle between the normal and the incident ray + * \param eta + * Relative refractive index (real component) + * \param k + * Relative refractive index (imaginary component) + * \ingroup libpython + */ +extern MTS_EXPORT_CORE Float fresnelConductorExact(Float cosThetaI, + Float eta, Float k); + +/** + * \brief Calculates the unpolarized Fresnel reflection coefficient + * at a planar interface having a complex-valued relative index of + * refraction (accurate vectorized version) + * + * The implementation of this function computes the exact unpolarized + * Fresnel reflectance for a complex index of refraction change. + * + * The name of this function is a slight misnomer, since it supports + * the general case of a complex-valued relative index of refraction + * (rather than being restricted to conductors) + * + * \param cosThetaI + * Cosine of the angle between the normal and the incident ray + * \param eta + * Relative refractive index (real component) + * \param k + * Relative refractive index (imaginary component) + * \ingroup libpython + */ +extern MTS_EXPORT_CORE Spectrum fresnelConductorExact(Float cosThetaI, + const Spectrum &eta, const Spectrum &k); + +/** + * \brief Calculates the diffuse unpolarized Fresnel reflectance of * a dielectric material (sometimes referred to as "Fdr"). * * This value quantifies what fraction of diffuse incident illumination diff --git a/include/mitsuba/hw/font.h b/include/mitsuba/hw/font.h index 55ed0ac0..c157237d 100644 --- a/include/mitsuba/hw/font.h +++ b/include/mitsuba/hw/font.h @@ -76,12 +76,22 @@ public: /// Allocate memory for a certain font Font(EFont font); + /// Draw text to the specified bitmap + void drawText(Bitmap *dest, Point2i pos, const std::string &text) const; + + /// Compute the size covered by the given string when rendered using this font + Vector2i getSize(const std::string &text) const; + /// Upload the font to the GPU void init(Renderer *renderer); /// Free the GPU memory void cleanup(); + /// Convert the underlying bitmap to a different pixel format + void convert(Bitmap::EPixelFormat pixelFormat, + Bitmap::EComponentFormat componentFormat, Float gamma); + /// Return the name of this font inline const std::string &getName() const { return m_name; } diff --git a/include/mitsuba/render/texture.h b/include/mitsuba/render/texture.h index f53f325e..05108c39 100644 --- a/include/mitsuba/render/texture.h +++ b/include/mitsuba/render/texture.h @@ -74,6 +74,9 @@ public: /// Serialize to a binary data stream virtual void serialize(Stream *stream, InstanceManager *manager) const; + /// Return the underlying bitmap representation (if any) + virtual ref getBitmap() const; + MTS_DECLARE_CLASS() protected: Texture(const Properties &props); diff --git a/src/bsdfs/conductor.cpp b/src/bsdfs/conductor.cpp index 061c44b9..d7042176 100644 --- a/src/bsdfs/conductor.cpp +++ b/src/bsdfs/conductor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "ior.h" MTS_NAMESPACE_BEGIN @@ -29,11 +30,12 @@ MTS_NAMESPACE_BEGIN * \parameters{ * \parameter{material}{\String}{Name of a material preset, see * \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}} - * \parameter{eta}{\Spectrum}{Real part of the material's index - * of refraction \default{based on the value of \texttt{material}}} - * \parameter{k}{\Spectrum}{Imaginary part of the material's index of - * refraction, also known as absorption coefficient. - * \default{based on the value of \texttt{material}}} + * \parameter{eta, k}{\Spectrum}{Real and imaginary components of the material's index of + * refraction \default{based on the value of \texttt{material}}} + * \parameter{extEta}{\Float\Or\String}{ + * Real-valued index of refraction of the surrounding dielectric, + * or a material name of a dielectric \default{\code{air}} + * } * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor that can be used to modulate the specular reflection component. Note * that for physical realism, this parameter should never be touched. \default{1.0}} @@ -154,21 +156,23 @@ public: m_specularReflectance = new ConstantSpectrumTexture( props.getSpectrum("specularReflectance", Spectrum(1.0f))); - std::string material = props.getString("material", "Cu"); + std::string materialName = props.getString("material", "Cu"); - Spectrum materialEta, materialK; - if (boost::to_lower_copy(material) == "none") { - materialEta = Spectrum(0.0f); - materialK = Spectrum(1.0f); + Spectrum intEta, intK; + if (boost::to_lower_copy(materialName) == "none") { + intEta = Spectrum(0.0f); + intK = Spectrum(1.0f); } else { - materialEta.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".eta.spd"))); - materialK.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".k.spd"))); + intEta.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".eta.spd"))); + intK.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".k.spd"))); } - m_eta = props.getSpectrum("eta", materialEta); - m_k = props.getSpectrum("k", materialK); + Float extEta = lookupIOR(props, "extEta", "air"); + + m_eta = props.getSpectrum("eta", intEta) / extEta; + m_k = props.getSpectrum("k", intK) / extEta; } SmoothConductor(Stream *stream, InstanceManager *manager) @@ -229,7 +233,7 @@ public: return Spectrum(0.0f); return m_specularReflectance->eval(bRec.its) * - fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + fresnelConductorExact(Frame::cosTheta(bRec.wi), m_eta, m_k); } Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const { @@ -260,7 +264,7 @@ public: bRec.eta = 1.0f; return m_specularReflectance->eval(bRec.its) * - fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + fresnelConductorExact(Frame::cosTheta(bRec.wi), m_eta, m_k); } Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const { @@ -277,7 +281,7 @@ public: pdf = 1; return m_specularReflectance->eval(bRec.its) * - fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + fresnelConductorExact(Frame::cosTheta(bRec.wi), m_eta, m_k); } Float getRoughness(const Intersection &its, int component) const { @@ -320,7 +324,7 @@ public: m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.get()); /* Compute the reflectance at perpendicular incidence */ - m_R0 = fresnelConductor(1.0f, eta, k); + m_R0 = fresnelConductorExact(1.0f, eta, k); m_alpha = 0.4f; } diff --git a/src/bsdfs/roughconductor.cpp b/src/bsdfs/roughconductor.cpp index f25b5de8..ca70bb2e 100644 --- a/src/bsdfs/roughconductor.cpp +++ b/src/bsdfs/roughconductor.cpp @@ -20,6 +20,7 @@ #include #include #include "microfacet.h" +#include "ior.h" MTS_NAMESPACE_BEGIN @@ -32,15 +33,15 @@ MTS_NAMESPACE_BEGIN * used to model the surface roughness. * \begin{enumerate}[(i)] * \item \code{beckmann}: Physically-based distribution derived from - * Gaussian random surfaces. This is the default. + * Gaussian random surfaces. This is the default.\vspace{-1mm} * \item \code{ggx}: New distribution proposed by * Walter et al. \cite{Walter07Microfacet}, which is meant to better handle * the long tails observed in measurements of ground surfaces. - * Renderings with this distribution may converge slowly. + * Renderings with this distribution may converge slowly.\vspace{-1mm} * \item \code{phong}: Classical $\cos^p\theta$ distribution. * Due to the underlying microfacet theory, * the use of this distribution here leads to more realistic - * behavior than the separately available \pluginref{phong} plugin. + * behavior than the separately available \pluginref{phong} plugin.\vspace{-1mm} * \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by * Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm} * \end{enumerate} @@ -59,11 +60,12 @@ MTS_NAMESPACE_BEGIN * } * \parameter{material}{\String}{Name of a material preset, see * \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}} - * \parameter{eta}{\Spectrum}{Real part of the material's index - * of refraction \default{based on the value of \texttt{material}}} - * \parameter{k}{\Spectrum}{Imaginary part of the material's index of - * refraction (the absorption coefficient). - * \default{based on \texttt{material}}} + * \parameter{eta, k}{\Spectrum}{Real and imaginary components of the material's index of + * refraction \default{based on the value of \texttt{material}}} + * \parameter{extEta}{\Float\Or\String}{ + * Real-valued index of refraction of the surrounding dielectric, + * or a material name of a dielectric \default{\code{air}} + * } * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor that can be used to modulate the specular reflection component. Note * that for physical realism, this parameter should never be touched. \default{1.0}} @@ -158,21 +160,23 @@ public: m_specularReflectance = new ConstantSpectrumTexture( props.getSpectrum("specularReflectance", Spectrum(1.0f))); - std::string material = props.getString("material", "Cu"); - Spectrum materialEta, materialK; + std::string materialName = props.getString("material", "Cu"); - if (boost::to_lower_copy(material) == "none") { - materialEta = Spectrum(0.0f); - materialK = Spectrum(1.0f); + Spectrum intEta, intK; + if (boost::to_lower_copy(materialName) == "none") { + intEta = Spectrum(0.0f); + intK = Spectrum(1.0f); } else { - materialEta.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".eta.spd"))); - materialK.fromContinuousSpectrum(InterpolatedSpectrum( - fResolver->resolve("data/ior/" + material + ".k.spd"))); + intEta.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".eta.spd"))); + intK.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + materialName + ".k.spd"))); } - m_eta = props.getSpectrum("eta", materialEta); - m_k = props.getSpectrum("k", materialK); + Float extEta = lookupIOR(props, "extEta", "air"); + + m_eta = props.getSpectrum("eta", intEta) / extEta; + m_k = props.getSpectrum("k", intK) / extEta; m_distribution = MicrofacetDistribution( props.getString("distribution", "beckmann") @@ -262,7 +266,7 @@ public: return Spectrum(0.0f); /* Fresnel factor */ - const Spectrum F = fresnelConductor(dot(bRec.wi, H), m_eta, m_k); + const Spectrum F = fresnelConductorExact(dot(bRec.wi, H), m_eta, m_k); /* Smith's shadow-masking function */ const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaU, alphaV); @@ -324,7 +328,7 @@ public: if (Frame::cosTheta(bRec.wo) <= 0) return Spectrum(0.0f); - const Spectrum F = fresnelConductor(dot(bRec.wi, m), + const Spectrum F = fresnelConductorExact(dot(bRec.wi, m), m_eta, m_k); Float numerator = m_distribution.eval(m, alphaU, alphaV) @@ -367,7 +371,7 @@ public: if (Frame::cosTheta(bRec.wo) <= 0) return Spectrum(0.0f); - const Spectrum F = fresnelConductor(dot(bRec.wi, m), + const Spectrum F = fresnelConductorExact(dot(bRec.wi, m), m_eta, m_k); Float numerator = m_distribution.eval(m, alphaU, alphaV) @@ -460,7 +464,7 @@ public: m_alphaVShader = renderer->registerShaderForResource(m_alphaV.get()); /* Compute the reflectance at perpendicular incidence */ - m_R0 = fresnelConductor(1.0f, eta, k); + m_R0 = fresnelConductorExact(1.0f, eta, k); } bool isComplete() const { diff --git a/src/films/hdrfilm.cpp b/src/films/hdrfilm.cpp index adac3f14..967e5900 100644 --- a/src/films/hdrfilm.cpp +++ b/src/films/hdrfilm.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "banner.h" @@ -114,7 +115,33 @@ MTS_NAMESPACE_BEGIN * * * \end{xml} - + * + * \subsubsection*{Annotations:} + * \label{sec:film-annotations} + * The \pluginref{ldrfilm} and \pluginref{hdrfilm} plugins support an additional + * feature referred to as \emph{annotations}, which can be quite useful under + * certain circumstances. + * + * Annotations are used to embed useful information inside a rendered image so + * that this information is later available to anyone viewing the image. + * Exemplary uses of this feature might be to store the frame or take number, + * camera parameters, or other relevant scene information. + * + * Annotations can either be created by means of a \emph{tag}, which is an entry + * in the metadata table of the image file (does not modify the actual image data), + * or a \emph{text} label which is ``burned'' into the image. + * + * The syntax of this looks as follows: + * + * \begin{xml} + * + * + * + * + * + * + * + * \end{xml} */ class HDRFilm : public Film { public: @@ -204,6 +231,25 @@ public: } + std::vector keys = props.getPropertyNames(); + for (size_t i=0; i args = tokenize(key.substr(5, key.length()-6), " ,"); + + if (args.size() != 2) + Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str()); + + Annotation annotation; + annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str())); + annotation.text = props.getString(keys[i]); + m_annotations.push_back(annotation); + } + } + m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize); } @@ -321,6 +367,23 @@ public: } } + if (!m_annotations.empty()) { + ref font = new Font(Font::EBitstreamVeraMono14); + font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), 1.0f); + + for (size_t i=0; igetSize(text); + bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f)); + font->drawText(bitmap, offset, text); + } + } + + for (std::map::const_iterator it = m_tags.begin(); + it != m_tags.end(); ++it) + bitmap->getMetadata()[it->first] = it->second; + fs::path filename = m_destFile; std::string extension = boost::to_lower_copy(filename.extension().string()); std::string properExtension = (m_fileFormat == Bitmap::EOpenEXR) ? ".exr" : ".rgbe"; @@ -367,6 +430,11 @@ public: MTS_DECLARE_CLASS() protected: + struct Annotation { + Point2i offset; + std::string text; + }; + Bitmap::EFileFormat m_fileFormat; Bitmap::EPixelFormat m_pixelFormat; Bitmap::EComponentFormat m_componentFormat; @@ -374,6 +442,9 @@ protected: bool m_attachLog; fs::path m_destFile; ref m_storage; + + std::vector m_annotations; + std::map m_tags; }; MTS_IMPLEMENT_CLASS_S(HDRFilm, false, Film) diff --git a/src/films/ldrfilm.cpp b/src/films/ldrfilm.cpp index b26f2ca1..eab41b11 100644 --- a/src/films/ldrfilm.cpp +++ b/src/films/ldrfilm.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "banner.h" @@ -113,6 +114,9 @@ MTS_NAMESPACE_BEGIN * The RGB values exported by this plugin correspond to the ITU-R Rec. BT. 709-3 * primaries with a D65 white point. When $\texttt{gamma}$ is set to $\code{-1}$ (the default), * the output is in the sRGB color space and will display as intended on compatible devices. + * + * Note that this plugin supports render-time \emph{annotations}, which + * are described on page~\pageref{sec:film-annotations}. */ class LDRFilm : public Film { public: @@ -176,6 +180,25 @@ public: m_reinhardKey = props.getFloat("key", 0.18f); m_reinhardBurn = props.getFloat("burn", 0.0); + std::vector keys = props.getPropertyNames(); + for (size_t i=0; i args = tokenize(key.substr(5, key.length()-6), " ,"); + + if (args.size() != 2) + Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str()); + + Annotation annotation; + annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str())); + annotation.text = props.getString(keys[i]); + m_annotations.push_back(annotation); + } + } + m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize); } @@ -313,6 +336,23 @@ public: } } + if (!m_annotations.empty()) { + ref font = new Font(Font::EBitstreamVeraMono14); + font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), m_gamma); + + for (size_t i=0; igetSize(text); + bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f)); + font->drawText(bitmap, offset, text); + } + } + + for (std::map::const_iterator it = m_tags.begin(); + it != m_tags.end(); ++it) + bitmap->getMetadata()[it->first] = it->second; + fs::path filename = m_destFile; std::string extension = boost::to_lower_copy(filename.extension().string()); std::string expectedExtension; @@ -367,6 +407,11 @@ public: MTS_DECLARE_CLASS() protected: + struct Annotation { + Point2i offset; + std::string text; + }; + Bitmap::EFileFormat m_fileFormat; Bitmap::EPixelFormat m_pixelFormat; bool m_hasBanner; @@ -375,6 +420,9 @@ protected: ref m_storage; ETonemapMethod m_tonemapMethod; Float m_exposure, m_reinhardKey, m_reinhardBurn; + + std::vector m_annotations; + std::map m_tags; }; MTS_IMPLEMENT_CLASS_S(LDRFilm, false, Film) diff --git a/src/films/tiledhdrfilm.cpp b/src/films/tiledhdrfilm.cpp index 0f15b59e..73751155 100644 --- a/src/films/tiledhdrfilm.cpp +++ b/src/films/tiledhdrfilm.cpp @@ -432,7 +432,7 @@ public: bool develop(const Point2i &sourceOffset, const Vector2i &size, const Point2i &targetOffset, Bitmap *target) const { - target->fill(targetOffset, size, Spectrum(0.0f)); + target->fillRect(targetOffset, size, Spectrum(0.0f)); return false; /* Not supported by the tiled EXR film! */ } diff --git a/src/integrators/path/volpath.cpp b/src/integrators/path/volpath.cpp index bc1fa419..9b1201a4 100644 --- a/src/integrators/path/volpath.cpp +++ b/src/integrators/path/volpath.cpp @@ -130,8 +130,7 @@ public: if (phaseVal != 0) { /* Calculate prob. of having sampled that direction using phase function sampling */ - Float phasePdf = (emitter->isOnSurface() && dRec.measure == ESolidAngle - && interactions == 0) + Float phasePdf = (emitter->isOnSurface() && dRec.measure == ESolidAngle) ? phase->pdf(pRec) : (Float) 0.0f; /* Weight using the power heuristic */ diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp index a29cc62d..da604825 100644 --- a/src/libcore/bitmap.cpp +++ b/src/libcore/bitmap.cpp @@ -430,47 +430,59 @@ void Bitmap::flipVertically() { } } -void Bitmap::accumulate(const Bitmap *bitmap, const Point2i &offset) { + +void Bitmap::accumulate(const Bitmap *bitmap, Point2i sourceOffset, + Point2i targetOffset, Vector2i size) { Assert(getPixelFormat() == bitmap->getPixelFormat() && getComponentFormat() == bitmap->getComponentFormat() && getChannelCount() == bitmap->getChannelCount()); - const int - offsetX = std::max(offset.x, 0), - offsetY = std::max(offset.y, 0), - endX = std::min(offset.x + bitmap->getSize().x, m_size.x), - endY = std::min(offset.y + bitmap->getSize().y, m_size.y); + Vector2i offsetIncrease( + std::max(0, std::max(-sourceOffset.x, -targetOffset.x)), + std::max(0, std::max(-sourceOffset.y, -targetOffset.y)) + ); - if (offsetX >= endX || offsetY >= endY) + sourceOffset += offsetIncrease; + targetOffset += offsetIncrease; + size -= offsetIncrease; + + Vector2i sizeDecrease( + std::max(0, std::max(sourceOffset.x + size.x - bitmap->getWidth(), targetOffset.x + size.x - getWidth())), + std::max(0, std::max(sourceOffset.y + size.y - bitmap->getHeight(), targetOffset.y + size.y - getHeight()))); + + size -= sizeDecrease; + + if (size.x <= 0 || size.y <= 0) return; const size_t - columns = (endX - offsetX) * m_channelCount, + columns = size.x * m_channelCount, pixelStride = getBytesPerPixel(), - sourceStride = bitmap->getSize().x * pixelStride, - targetStride = m_size.x * pixelStride; + sourceStride = bitmap->getWidth() * pixelStride, + targetStride = getWidth() * pixelStride; const uint8_t *source = bitmap->getUInt8Data() + - (offsetX - offset.x + (offsetY - offset.y) * bitmap->getSize().x) * pixelStride; + (sourceOffset.x + sourceOffset.y * (size_t) bitmap->getWidth()) * pixelStride; uint8_t *target = m_data + - (offsetX + offsetY * m_size.x) * pixelStride; + (targetOffset.x + targetOffset.y * (size_t) m_size.x) * pixelStride; - for (int y = offsetY; y < endY; ++y) { + for (int y = 0; y < size.y; ++y) { switch (m_componentFormat) { case EUInt8: for (size_t i = 0; i < columns; ++i) - ((uint8_t *) target)[i] += ((uint8_t *) source)[i]; + ((uint8_t *) target)[i] = (uint8_t) std::min(0xFF, ((uint8_t *) source)[i] + ((uint8_t *) target)[i]); + break; case EUInt16: for (size_t i = 0; i < columns; ++i) - ((uint16_t *) target)[i] += ((uint16_t *) source)[i]; + ((uint16_t *) target)[i] = (uint16_t) std::min(0xFFFF, ((uint16_t *) source)[i] + ((uint16_t *) target)[i]); break; case EUInt32: for (size_t i = 0; i < columns; ++i) - ((uint32_t *) target)[i] += ((uint32_t *) source)[i]; + ((uint32_t *) target)[i] = std::min((uint32_t) 0xFFFFFFFFUL, ((uint32_t *) source)[i] + ((uint32_t *) target)[i]); break; case EFloat16: @@ -554,8 +566,9 @@ void Bitmap::setPixel(const Point2i &pos, const Spectrum &value) { } void Bitmap::drawHLine(int y, int x1, int x2, const Spectrum &value) { - AssertEx( y >= 0 && y < m_size.y && - x1 >= 0 && x2 < m_size.x, "Bitmap::drawVLine(): out of bounds!"); + if (y < 0 || y >= m_size.y) + return; + x1 = std::max(x1, 0); x2 = std::min(x2, m_size.x-1); const FormatConverter *cvt = FormatConverter::getInstance( std::make_pair(EFloat, m_componentFormat) @@ -574,8 +587,9 @@ void Bitmap::drawHLine(int y, int x1, int x2, const Spectrum &value) { } void Bitmap::drawVLine(int x, int y1, int y2, const Spectrum &value) { - AssertEx( x >= 0 && x < m_size.x && - y1 >= 0 && y2 < m_size.y, "Bitmap::drawVLine(): out of bounds!"); + if (x < 0 || x >= m_size.x) + return; + y1 = std::max(y1, 0); y2 = std::min(y2, m_size.y-1); const FormatConverter *cvt = FormatConverter::getInstance( std::make_pair(EFloat, m_componentFormat) @@ -601,9 +615,12 @@ void Bitmap::drawRect(const Point2i &offset, const Vector2i &size, const Spectru drawVLine(offset.x + size.x - 1, offset.y, offset.y + size.y - 1, value); } -void Bitmap::fill(const Point2i &offset, const Vector2i &size, const Spectrum &value) { - AssertEx(offset.x >= 0 && offset.x + size.x <= m_size.x && - offset.y >= 0 && offset.y + size.y <= m_size.y, "Bitmap::fill(): out of bounds!"); +void Bitmap::fillRect(Point2i offset, Vector2i size, const Spectrum &value) { + int sx = std::max(0, -offset.x), sy = std::max(0, -offset.y); + size.x -= sx; size.y -= sy; offset.x += sx; offset.y += sy; + + size.x -= std::max(0, offset.x + size.x - m_size.x); + size.y -= std::max(0, offset.y + size.y - m_size.y); const FormatConverter *cvt = FormatConverter::getInstance( std::make_pair(EFloat, m_componentFormat) @@ -995,6 +1012,79 @@ ref Bitmap::crop(const Point2i &offset, const Vector2i &size) const { return result; } +void Bitmap::applyMatrix(Float matrix_[3][3]) { + int stride = 0; + + if (m_pixelFormat == ERGB || m_pixelFormat == EXYZ) + stride = 3; + else if (m_pixelFormat == ERGBA || m_pixelFormat == EXYZA) + stride = 4; + else + Log(EError, "Bitmap::applyMatrix(): unsupported pixel format!"); + + size_t pixels = (size_t) m_size.x * (size_t) m_size.y; + + switch (m_componentFormat) { + case EFloat16: { + float matrix[3][3]; + half *data = getFloat16Data(); + for (int i=0; i<3; ++i) + for (int j=0; j<3; ++j) + matrix[i][j] = (float) matrix_[i][j]; + + for (size_t i=0; i static void resample(const ReconstructionFilter *rfilter, ReconstructionFilter::EBoundaryCondition bch, diff --git a/src/libcore/class.cpp b/src/libcore/class.cpp index 954b4f05..5fb696df 100644 --- a/src/libcore/class.cpp +++ b/src/libcore/class.cpp @@ -78,9 +78,9 @@ void Class::initializeOnce(Class *theClass) { } void Class::staticInitialization() { - std::for_each(__classes->begin(), __classes->end(), - compose1(std::ptr_fun(initializeOnce), - select2nd())); + for (ClassMap::iterator it = __classes->begin(); + it != __classes->end(); ++it) + initializeOnce(it->second); m_isInitialized = true; } diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index d24e6063..6ecbff78 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -586,18 +586,84 @@ Float fresnelDielectricExt(Float cosThetaI_, Float &cosThetaT_, Float eta) { return 0.5f * (Rs * Rs + Rp * Rp); } -Spectrum fresnelConductor(Float cosThetaI, const Spectrum &eta, const Spectrum &k) { - Spectrum tmp = (eta*eta + k*k) * (cosThetaI * cosThetaI); +Float fresnelConductorApprox(Float cosThetaI, Float eta, Float k) { + Float cosThetaI2 = cosThetaI*cosThetaI; - Spectrum rParl2 = (tmp - (eta * (2.0f * cosThetaI)) + Spectrum(1.0f)) - / (tmp + (eta * (2.0f * cosThetaI)) + Spectrum(1.0f)); + Float tmp = (eta*eta + k*k) * cosThetaI2; + + Float Rp2 = (tmp - (eta * (2 * cosThetaI)) + 1) + / (tmp + (eta * (2 * cosThetaI)) + 1); + + Float tmpF = eta*eta + k*k; + + Float Rs2 = (tmpF - (eta * (2 * cosThetaI)) + cosThetaI2) / + (tmpF + (eta * (2 * cosThetaI)) + cosThetaI2); + + return 0.5f * (Rp2 + Rs2); +} + +Spectrum fresnelConductorApprox(Float cosThetaI, const Spectrum &eta, const Spectrum &k) { + Float cosThetaI2 = cosThetaI*cosThetaI; + + Spectrum tmp = (eta*eta + k*k) * cosThetaI2; + + Spectrum Rp2 = (tmp - (eta * (2 * cosThetaI)) + Spectrum(1.0f)) + / (tmp + (eta * (2 * cosThetaI)) + Spectrum(1.0f)); Spectrum tmpF = eta*eta + k*k; - Spectrum rPerp2 = (tmpF - (eta * (2.0f * cosThetaI)) + Spectrum(cosThetaI*cosThetaI)) / - (tmpF + (eta * (2.0f * cosThetaI)) + Spectrum(cosThetaI*cosThetaI)); + Spectrum Rs2 = (tmpF - (eta * (2 * cosThetaI)) + Spectrum(cosThetaI2)) / + (tmpF + (eta * (2 * cosThetaI)) + Spectrum(cosThetaI2)); - return (rParl2 + rPerp2) / 2.0f; + return 0.5f * (Rp2 + Rs2); +} + +Float fresnelConductorExact(Float cosThetaI, Float eta, Float k) { + /* Modified from "Optics" by K.D. Moeller, University Science Books, 1988 */ + + Float cosThetaI2 = cosThetaI*cosThetaI, + sinThetaI2 = 1-cosThetaI2, + sinThetaI4 = sinThetaI2*sinThetaI2; + + Float temp1 = eta*eta - k*k - sinThetaI2, + a2pb2 = math::safe_sqrt(temp1*temp1 + 4*k*k*eta*eta), + a = math::safe_sqrt(0.5f * (a2pb2 + temp1)); + + Float term1 = a2pb2 + cosThetaI2, + term2 = 2*a*cosThetaI; + + Float Rs2 = (term1 - term2) / (term1 + term2); + + Float term3 = a2pb2*cosThetaI2 + sinThetaI4, + term4 = term2*sinThetaI2; + + Float Rp2 = Rs2 * (term3 - term4) / (term3 + term4); + + return 0.5f * (Rp2 + Rs2); +} + +Spectrum fresnelConductorExact(Float cosThetaI, const Spectrum &eta, const Spectrum &k) { + /* Modified from "Optics" by K.D. Moeller, University Science Books, 1988 */ + + Float cosThetaI2 = cosThetaI*cosThetaI, + sinThetaI2 = 1-cosThetaI2, + sinThetaI4 = sinThetaI2*sinThetaI2; + + Spectrum temp1 = eta*eta - k*k - Spectrum(sinThetaI2), + a2pb2 = (temp1*temp1 + k*k*eta*eta*4).safe_sqrt(), + a = ((a2pb2 + temp1) * 0.5f).safe_sqrt(); + + Spectrum term1 = a2pb2 + Spectrum(cosThetaI2), + term2 = a*(2*cosThetaI); + + Spectrum Rs2 = (term1 - term2) / (term1 + term2); + + Spectrum term3 = a2pb2*cosThetaI2 + Spectrum(sinThetaI4), + term4 = term2*sinThetaI2; + + Spectrum Rp2 = Rs2 * (term3 - term4) / (term3 + term4); + + return 0.5f * (Rp2 + Rs2); } Vector reflect(const Vector &wi, const Normal &n) { diff --git a/src/libhw/font.cpp b/src/libhw/font.cpp index dc705f3c..8f288c40 100644 --- a/src/libhw/font.cpp +++ b/src/libhw/font.cpp @@ -74,6 +74,70 @@ Font::Font(EFont font) { dscStream->read(m_kerningMatrix, 256*256); } +void Font::convert(Bitmap::EPixelFormat pixelFormat, Bitmap::EComponentFormat componentFormat, Float gamma) { + m_bitmap = m_bitmap->convert(pixelFormat, componentFormat, gamma); +} + +void Font::drawText(Bitmap *dest, Point2i pos, const std::string &text) const { + int initial = pos.x; + + for (size_t i=0; igetWidth(), + glyph.tx.y * m_bitmap->getHeight()); + + dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size); + + pos.x += glyph.horizontalAdvance; + + if (i+1 < text.length()) + pos.x += getKerning(character, text[i+1]); + } +} + +Vector2i Font::getSize(const std::string &text) const { + Vector2i size(0, getMaxVerticalBearing()); + int pos = 0; + + for (size_t i=0; icreateGPUTexture(m_name, m_bitmap); m_texture->setFilterType(GPUTexture::ENearest); diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp index d7f85859..0e040329 100644 --- a/src/libpython/core.cpp +++ b/src/libpython/core.cpp @@ -79,10 +79,10 @@ void initializeFramework() { if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) &initializeFramework, &hm)) { std::vector lpFilename(MAX_PATH); - + // Try to get the path with the default MAX_PATH length (260 chars) DWORD nSize = GetModuleFileNameW(hm, &lpFilename[0], MAX_PATH); - + // Adjust the buffer size in case if was too short while (nSize == lpFilename.size()) { lpFilename.resize(nSize * 2); @@ -175,7 +175,7 @@ public: bp::extract extractTransform(value); bp::extract extractSpectrum(value); - if (extractString.check()) { + if (extractString.check()){ props.setString(name, extractString()); } else if (extractBoolean.check() && PyObject_IsInstance(value.ptr(), (PyObject *) &PyBool_Type)) { props.setBoolean(name, extractBoolean()); @@ -205,6 +205,12 @@ struct path_to_python_str { }; +struct TSpectrum_to_Spectrum { + static PyObject* convert(const TSpectrum &spectrum) { + return bp::incref(bp::object(Spectrum(spectrum)).ptr()); + } +}; + static void Matrix4x4_setItem(Matrix4x4 *matrix, bp::tuple tuple, Float value) { if (bp::len(tuple) != 2) SLog(EError, "Invalid matrix indexing operation, required a tuple of length 2"); @@ -472,6 +478,43 @@ Vector refract3(const Vector &wi, const Normal &n, Float eta) { return refract(wi, n, eta); } +void bitmap_applyMatrix(Bitmap *bitmap, bp::list list) { + int length = bp::len(list); + if (length != 9) + SLog(EError, "Require a color matrix specified as a list with 9 entries!"); + + Float matrix[3][3]; + + int idx = 0; + for (int i=0; i<3; ++i) + for (int j=0; j<3; ++j) + matrix[i][j] = bp::extract(list[idx++]); + + bitmap->applyMatrix(matrix); +} + +void bitmap_write(Bitmap *bitmap, Bitmap::EFileFormat fmt, Stream *stream) { + bitmap->write(fmt, stream); +} + +ref bitmap_convert_1(Bitmap *bitmap, Bitmap::EPixelFormat pixelFormat, Bitmap::EComponentFormat componentFormat, + Float gamma, Float multiplier, Spectrum::EConversionIntent intent) { + return bitmap->convert(pixelFormat, componentFormat, gamma, multiplier, intent); +} + +ref bitmap_convert_2(Bitmap *bitmap, Bitmap::EPixelFormat pixelFormat, Bitmap::EComponentFormat componentFormat, + Float gamma, Float multiplier) { + return bitmap->convert(pixelFormat, componentFormat, gamma, multiplier); +} + +ref bitmap_convert_3(Bitmap *bitmap, Bitmap::EPixelFormat pixelFormat, Bitmap::EComponentFormat componentFormat, + Float gamma) { + return bitmap->convert(pixelFormat, componentFormat, gamma); +} + +ref bitmap_convert_4(Bitmap *bitmap, Bitmap::EPixelFormat pixelFormat, Bitmap::EComponentFormat componentFormat) { + return bitmap->convert(pixelFormat, componentFormat); +} Transform transform_glOrthographic1(Float clipNear, Float clipFar) { return Transform::glOrthographic(clipNear, clipFar); @@ -489,6 +532,7 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4) void export_core() { bp::to_python_converter(); + bp::to_python_converter, TSpectrum_to_Spectrum>(); bp::implicitly_convertible(); bp::object coreModule( @@ -708,17 +752,23 @@ void export_core() { .def("clear", &InterpolatedSpectrum::clear) .def("zeroExtend", &InterpolatedSpectrum::zeroExtend); + void (Bitmap::*accumulate_1)(const Bitmap *bitmap, Point2i sourceOffset, Point2i targetOffset, Vector2i size) = &Bitmap::accumulate; + void (Bitmap::*accumulate_2)(const Bitmap *bitmap, Point2i targetOffset) = &Bitmap::accumulate; + BP_CLASS(Bitmap, Object, (bp::init())) .def(bp::init()) .def(bp::init()) .def("clone", &Bitmap::clone, BP_RETURN_VALUE) + .def("clear", &Bitmap::clear) .def("separateChannel", &Bitmap::separateChannel, BP_RETURN_VALUE) .def("expand", &Bitmap::expand, BP_RETURN_VALUE) .def("flipVertically", &Bitmap::flipVertically) .def("crop", &Bitmap::crop) - .def("accumulate", &Bitmap::accumulate) - .def("clear", &Bitmap::clear) - .def("write", &Bitmap::write) + .def("applyMatrix", &bitmap_applyMatrix) + .def("colorBalance", &Bitmap::colorBalance) + .def("accumulate", accumulate_1) + .def("accumulate", accumulate_2) + .def("write", &bitmap_write) .def("setString", &Bitmap::setString) .def("getString", &Bitmap::getString, BP_RETURN_VALUE) .def("setGamma", &Bitmap::setGamma) @@ -732,9 +782,18 @@ void export_core() { .def("getBitsPerComponent", &Bitmap::getBitsPerComponent) .def("getBytesPerComponent", &Bitmap::getBytesPerComponent) .def("getBytesPerPixel", &Bitmap::getBytesPerPixel) + .def("getBufferSize", &Bitmap::getBufferSize) .def("getPixel", &Bitmap::getPixel, BP_RETURN_VALUE) .def("setPixel", &Bitmap::setPixel) - .def("getSize", &Bitmap::getSize, BP_RETURN_VALUE); + .def("drawHLine", &Bitmap::drawHLine) + .def("drawVLine", &Bitmap::drawVLine) + .def("drawRect", &Bitmap::drawRect) + .def("fillRect", &Bitmap::fillRect) + .def("getSize", &Bitmap::getSize, BP_RETURN_VALUE) + .def("convert", &bitmap_convert_1, BP_RETURN_VALUE) + .def("convert", &bitmap_convert_2, BP_RETURN_VALUE) + .def("convert", &bitmap_convert_3, BP_RETURN_VALUE) + .def("convert", &bitmap_convert_4, BP_RETURN_VALUE); BP_SETSCOPE(Bitmap_class); bp::enum_("EPixelFormat") @@ -745,7 +804,8 @@ void export_core() { .value("ESpectrum", Bitmap::ESpectrum) .value("ESpectrumAlpha", Bitmap::ESpectrumAlpha) .value("ESpectrumAlphaWeight", Bitmap::ESpectrumAlphaWeight) - .value("EMultiChannel", Bitmap::EMultiChannel); + .value("EMultiChannel", Bitmap::EMultiChannel) + .export_values(); bp::enum_("EComponentFormat") .value("EBitmask", Bitmap::EBitmask) @@ -763,10 +823,13 @@ void export_core() { .value("EPNG", Bitmap::EPNG) .value("EOpenEXR", Bitmap::EOpenEXR) .value("ETGA", Bitmap::ETGA) + .value("EPFM", Bitmap::EPFM) + .value("ERGBE", Bitmap::ERGBE) .value("EBMP", Bitmap::EBMP) .value("EJPEG", Bitmap::EJPEG) .value("EAuto", Bitmap::EAuto) .export_values(); + BP_SETSCOPE(coreModule); BP_CLASS(FileResolver, Object, bp::init<>()) @@ -1261,11 +1324,19 @@ void export_core() { .staticmethod("glOrthographic") .staticmethod("fromFrame"); + Float (*fresnelConductorApprox1)(Float, Float, Float) = &fresnelConductorApprox; + Float (*fresnelConductorExact1)(Float, Float, Float) = &fresnelConductorExact; + Spectrum (*fresnelConductorApprox2)(Float, const Spectrum &, const Spectrum &) = &fresnelConductorApprox; + Spectrum (*fresnelConductorExact2)(Float, const Spectrum &, const Spectrum &) = &fresnelConductorExact; + /* Functions from utility.h */ bp::def("fresnelDielectric", &fresnelDielectric); bp::def("fresnelDielectricExt", &fresnelDielectricExt1); bp::def("fresnelDielectricExt", &fresnelDielectricExt2); - bp::def("fresnelConductor", &fresnelConductor, BP_RETURN_VALUE); + bp::def("fresnelConductorApprox", fresnelConductorApprox1, BP_RETURN_VALUE); + bp::def("fresnelConductorApprox", fresnelConductorApprox2, BP_RETURN_VALUE); + bp::def("fresnelConductorExact", fresnelConductorExact1, BP_RETURN_VALUE); + bp::def("fresnelConductorExact", fresnelConductorExact2, BP_RETURN_VALUE); bp::def("fresnelDiffuseReflectance", &fresnelDiffuseReflectance); bp::def("reflect", &reflect); bp::def("refract", &refract1); diff --git a/src/librender/texture.cpp b/src/librender/texture.cpp index 66797fb9..76221c71 100644 --- a/src/librender/texture.cpp +++ b/src/librender/texture.cpp @@ -47,6 +47,7 @@ Spectrum Texture::getMinimum() const { NotImplementedError("getMinimum"); } Spectrum Texture::getMaximum() const { NotImplementedError("getMaximum"); } bool Texture::isConstant() const { NotImplementedError("isConstant"); } bool Texture::usesRayDifferentials() const { NotImplementedError("usesRayDifferentials"); } +ref Texture::getBitmap() const { return NULL; } ref Texture::expand() { return this; diff --git a/src/mtsgui/SConscript b/src/mtsgui/SConscript index 19f0f37e..a0c70beb 100644 --- a/src/mtsgui/SConscript +++ b/src/mtsgui/SConscript @@ -33,9 +33,8 @@ if hasQt: del qtEnv['CXXFLAGS'][index-1] index = qtEnv['LINKFLAGS'].index('/SUBSYSTEM:CONSOLE') del qtEnv['LINKFLAGS'][index] - qtEnv.Append(CXXFLAGS=['/D', '_WINDOWS']) + qtEnv.Append(CXXFLAGS=['/D', '_WINDOWS', '/D', 'MTS_CUSTOM_QTMAIN', '/D', 'MTSGUI_STATIC_QFILEDIALOG=1']) qtEnv.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) - qtEnv.Append(LIBS=['qtmain']) if hasBreakpad: qtEnv.Append(CPPPATH=['#dependencies/include/breakpad']) qtEnv.Append(LIBS=['breakpad_common', 'breakpad_exception_handler', 'breakpad_crash_generation_client', @@ -72,6 +71,10 @@ if hasQt: qtSources += qtEnv_osx.StaticObject('breakpad.mm') else: qtSources = [x for x in qtSources if (not isinstance(x, str) or 'cocoa' not in x)] + + if sys.platform == 'win32': + qtSources += qtEnv.StaticObject('qtmain_win.cpp') + mtsgui = qtEnv.Program('mtsgui', qtSources) if sys.platform == 'darwin': qtEnv.AddPostAction(mtsgui, 'install_name_tool -change QtGui.framework/Versions/4/QtGui @rpath/QtGui $TARGET') diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp index e1722b2d..84e9811b 100644 --- a/src/mtsgui/mainwindow.cpp +++ b/src/mtsgui/mainwindow.cpp @@ -558,6 +558,8 @@ void MainWindow::on_actionOpen_triggered() { } } +void MainWindow::onOpenDialogClose(int reason) { /* unused */ } + #else // MTSGUI_STATIC_QFILEDIALOG void MainWindow::on_actionOpen_triggered() { @@ -1447,6 +1449,8 @@ void MainWindow::on_actionExportImage_triggered() { } } +void MainWindow::onExportDialogClose(int reason) { /* unused */ } + #else // MTSGUI_STATIC_QFILEDIALOG void MainWindow::on_actionExportImage_triggered() { @@ -1563,6 +1567,8 @@ void MainWindow::on_actionSaveAs_triggered() { } } +void MainWindow::onSaveAsDialogClose(int reason) { /* unused */ } + #else // MTSGUI_STATIC_QFILEDIALOG void MainWindow::on_actionSaveAs_triggered() { diff --git a/src/mtsgui/mainwindow.h b/src/mtsgui/mainwindow.h index 6e6afbbd..2ec338d7 100644 --- a/src/mtsgui/mainwindow.h +++ b/src/mtsgui/mainwindow.h @@ -192,11 +192,9 @@ private slots: void updateUI(); void updateStatus(); void onPreviewSettingsClose(); -#if !MTSGUI_STATIC_QFILEDIALOG void onOpenDialogClose(int reason); void onExportDialogClose(int reason); void onSaveAsDialogClose(int reason); -#endif void onRenderSettingsClose(int reason); void onImportDialogClose(int reason); void onSceneInformationClose(int reason); diff --git a/src/mtsgui/qtmain_win.cpp b/src/mtsgui/qtmain_win.cpp index a9919de4..a4735053 100644 --- a/src/mtsgui/qtmain_win.cpp +++ b/src/mtsgui/qtmain_win.cpp @@ -44,12 +44,12 @@ # error This source file can only be used in Windows builds #endif -#include -#include -#include +#include +#include +#include #define WIN32_LEAN_AND_MEAN -#include +#include /* This file contains the code in the qtmain library for Windows. diff --git a/src/subsurface/dipole.cpp b/src/subsurface/dipole.cpp index 379bd4e8..ebf2b982 100644 --- a/src/subsurface/dipole.cpp +++ b/src/subsurface/dipole.cpp @@ -208,9 +208,9 @@ static int irrOctreeIndex = 0; * rendered using diffusion theory and radiative transport, respectively. * The former produces an incorrect result, since the assumption of * many scattering events breaks down. - * \textbf{(c)}: When the number of irradiance samples is too low, the - * resulting noise becomes visible as ``blotchy'' artifacts in the - * rendering.} + * \textbf{(c)}: When the number of irradiance samples is too low when rendering + * with the dipole model, the resulting noise becomes visible as ``blotchy'' artifacts + * in the rendering.} * } * * \subsubsection*{Typical material setup} diff --git a/src/textures/bitmap.cpp b/src/textures/bitmap.cpp index ba80bd21..21f2f292 100644 --- a/src/textures/bitmap.cpp +++ b/src/textures/bitmap.cpp @@ -400,6 +400,10 @@ public: return result; } + ref getBitmap() const { + return m_mipmap1.get() ? m_mipmap1->toBitmap() : m_mipmap3->toBitmap(); + } + Spectrum eval(const Point2 &uv, const Vector2 &d0, const Vector2 &d1) const { stats::filteredLookups.incrementBase(); ++stats::filteredLookups; diff --git a/src/volume/gridvolume.cpp b/src/volume/gridvolume.cpp index 77132d9e..a08dae6e 100644 --- a/src/volume/gridvolume.cpp +++ b/src/volume/gridvolume.cpp @@ -93,7 +93,7 @@ MTS_NAMESPACE_BEGIN * * When using this data source to represent floating point density volumes, * please ensure that the values are all normalized to lie in the - * range $[0, 1]$---otherwise, the Woocock-Tracking integration method in + * range $[0, 1]$---otherwise, the Woodcock-Tracking integration method in * \pluginref{heterogeneous} will produce incorrect results. */ class GridDataSource : public VolumeDataSource {