diff --git a/build/SConscript.configure b/build/SConscript.configure index b344c02c..1cf9e465 100644 --- a/build/SConscript.configure +++ b/build/SConscript.configure @@ -201,7 +201,7 @@ env.Replace(CPPFLAGS=cppFlagsPrevious) env.Replace(CXXFLAGS=cxxFlagsPrevious) sys.stdout.write("Checking for Mitsuba version .. ") -file = open(env.GetBuildPath('#include/mitsuba/mitsuba.h'), 'r') +file = open(env.GetBuildPath('#include/mitsuba/core/version.h'), 'r') MTS_VERSION="" for line in file: if line.startswith("#define MTS_VERSION "): diff --git a/build/SConscript.install b/build/SConscript.install index fdf131d3..c6791581 100644 --- a/build/SConscript.install +++ b/build/SConscript.install @@ -79,7 +79,7 @@ if sys.platform == 'win32': if hasQt: install('#dist', ['qtgui/mtsgui.exe']) install('#dist', ['QtCore4.dll', 'QtGui4.dll', 'QtXml4.dll', - 'QtNetwork4.dll', 'QtOpenGL4.dll'], prefix = env['QT4_BINPATH']) + 'QtNetwork4.dll', 'QtOpenGL4.dll', 'QtXmlPatterns4.dll'], prefix = env['QT4_BINPATH']) elif sys.platform == 'darwin': for i in plugins: installTargets += env.Install('#Mitsuba.app/plugins', i) @@ -111,9 +111,11 @@ elif sys.platform == 'darwin': installTargets += env.OSXLibInst('#Mitsuba.app/Contents/Frameworks', '/Library/Frameworks/QtCore.framework/Versions/4/QtCore') opengl = env.OSXLibInst('#Mitsuba.app/Contents/Frameworks', '/Library/Frameworks/QtOpenGL.framework/Versions/4/QtOpenGL') xml = env.OSXLibInst('#Mitsuba.app/Contents/Frameworks', '/Library/Frameworks/QtXml.framework/Versions/4/QtXml') + xmlpatterns = env.OSXLibInst('#Mitsuba.app/Contents/Frameworks', '/Library/Frameworks/QtXmlPatterns.framework/Versions/4/QtXmlPatterns') network = env.OSXLibInst('#Mitsuba.app/Contents/Frameworks', '/Library/Frameworks/QtNetwork.framework/Versions/4/QtNetwork') gui = env.OSXLibInst('#Mitsuba.app/Contents/Frameworks', '/Library/Frameworks/QtGui.framework/Versions/4/QtGui') installTargets += env.AddPostAction(xml, 'install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore $TARGET') + installTargets += env.AddPostAction(xmlpatterns, 'install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore $TARGET') installTargets += env.AddPostAction(network, 'install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore $TARGET') installTargets += env.AddPostAction(gui, 'install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore $TARGET') installTargets += env.AddPostAction(opengl, 'install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore $TARGET') diff --git a/data/schema/upgrade_0.3.0.xsl b/data/schema/upgrade_0.3.0.xsl new file mode 100644 index 00000000..b6888bc2 --- /dev/null +++ b/data/schema/upgrade_0.3.0.xsl @@ -0,0 +1,198 @@ + + + + + + + + + + + 0.3.0 + + + + + + + + + + + , + + , + + + + + , + + , + + + + + + , + + , + + + + + + + + + + + + + + 1.0 + + + + + + + + 1.0 + + + + + 1.0 + 0.2 + 0.2 + + + + + 0.0 + 0.5 + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + value + + + + + + value + + + + + + + + + + + + + + + + + diffuse + + + + + roughplastic + + + alpha + + + + + conductor + + + + + + + + + + + roughconductor + + + alpha + + + eta + + + + + mixture + + + + + bitmap + + + + + bitmap + + + + + + + + + diff --git a/doc/images/bsdf_bump_1.jpg b/doc/images/bsdf_bump_1.jpg new file mode 100644 index 00000000..f7b89185 Binary files /dev/null and b/doc/images/bsdf_bump_1.jpg differ diff --git a/doc/images/bsdf_bump_2.jpg b/doc/images/bsdf_bump_2.jpg new file mode 100644 index 00000000..47f60d24 Binary files /dev/null and b/doc/images/bsdf_bump_2.jpg differ diff --git a/doc/main.tex b/doc/main.tex index 9087c1d7..4a204177 100644 --- a/doc/main.tex +++ b/doc/main.tex @@ -15,7 +15,7 @@ \usepackage{color} \usepackage{colortbl} \usepackage{listings} -\usepackage{subfigure} +\usepackage[hang]{subfigure} \usepackage{enumerate} \usepackage{ragged2e} % Ragged-right columns with hyphenation \usepackage{macros} diff --git a/include/mitsuba/core/fwd.h b/include/mitsuba/core/fwd.h index cef42b40..97b0d6d6 100644 --- a/include/mitsuba/core/fwd.h +++ b/include/mitsuba/core/fwd.h @@ -53,6 +53,7 @@ class Plugin; class PluginManager; class ProgressReporter; class Properties; +struct Version; class Random; struct Ray; struct RayDifferential; diff --git a/include/mitsuba/core/sched_remote.h b/include/mitsuba/core/sched_remote.h index dc90b969..0e6be587 100644 --- a/include/mitsuba/core/sched_remote.h +++ b/include/mitsuba/core/sched_remote.h @@ -21,14 +21,17 @@ #include +/// Default port of mtssrv +#define MTS_DEFAULT_PORT 7554 + /** How many work units should be sent to a remote worker at a time? This is a multiple of the worker's core count */ -#define BACKLOG_FACTOR 3 +#define MTS_BACKLOG_FACTOR 3 /** Once the back log factor drops below this value (also a multiple of the core size), the stream processor will continue sending batches of work units */ -#define CONTINUE_FACTOR 2 +#define MTS_CONTINUE_FACTOR 2 MTS_NAMESPACE_BEGIN diff --git a/include/mitsuba/core/version.h b/include/mitsuba/core/version.h new file mode 100644 index 00000000..39df4f55 --- /dev/null +++ b/include/mitsuba/core/version.h @@ -0,0 +1,107 @@ +/* + This file is part of Mitsuba, a physically based rendering system. + + Copyright (c) 2007-2011 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 . +*/ + +#if !defined(__PROGRAM_VERSION_H) +#define __PROGRAM_VERSION_H + +MTS_NAMESPACE_BEGIN + +/// Current release of Mitsuba +#define MTS_VERSION "0.3.0" + +// Year of this release +#define MTS_YEAR "2011" + +/** + * \brief A simple data structure for representing and comparing + * Mitsuba version strings + */ +struct Version { +public: + /// Default constructor: initialize to an invalid version (0.0.0) + inline Version() : m_major(0), m_minor(0), m_release(0) { } + + /// Initialize with the specified version number + inline Version(int major, int minor, int release) + : m_major(major), m_minor(minor), m_release(release) { } + + /** + * \brief Parse a version string of the form "major.minor.release" + * and turn it into a \ref Version structure + */ + Version(const std::string &versionString); + + /// Check if this program version is \a older than \c other + inline bool operator<(const Version &other) const { + if (m_major < other.m_major) + return true; + else if (m_major > other.m_major) + return false; + else if (m_minor < other.m_minor) + return true; + else if (m_minor > other.m_minor) + return false; + else if (m_release < other.m_release) + return true; + else + return false; + } + + /// Check if this program version is \a older than or equal to \c other + inline bool operator<=(const Version &other) const { + return *this < other || *this == other; + } + + /// Check if two program versions match + inline bool operator==(const Version &other) const { + return m_major == other.m_major + && m_minor == other.m_minor + && m_release == other.m_release; + } + + /// Is this a valid version number? + bool isValid() { + return m_major != 0 || m_minor != 0 || m_release != 0; + } + + /// Are the following two versions compatible? + bool isCompatible(const Version &other) const { + return m_major == other.m_major && + m_minor == other.m_minor; + } + + /// Turn into a string of the form "major.minor.release" + std::string toString() const; + + /// Return the major version + inline int getMajorVersion() const { return m_major; } + + /// Return the minor version + inline int getMinorVersion() const { return m_minor; } + + /// Return the release + inline int getRelease() const { return m_release; } +private: + int m_major; + int m_minor; + int m_release; +}; + +MTS_NAMESPACE_END + +#endif /* __PROGRAM_VERSION_H */ diff --git a/include/mitsuba/mitsuba.h b/include/mitsuba/mitsuba.h index 14d38b6c..cd062bce 100644 --- a/include/mitsuba/mitsuba.h +++ b/include/mitsuba/mitsuba.h @@ -34,16 +34,6 @@ #include #include -/// Current release of Mitsuba -#define MTS_VERSION "0.2.2" -#define MTS_VERSION_CODE 000201 - -/// Year of this release -#define MTS_YEAR "2011" - -/// Default port of mtssrv -#define MTS_DEFAULT_PORT 7554 - using std::cout; using std::cerr; using std::endl; @@ -51,6 +41,7 @@ using std::endl; /** * Include a basic subset of the core classes */ +#include #include #include #include diff --git a/include/mitsuba/render/scenehandler.h b/include/mitsuba/render/scenehandler.h index ea1cb3c8..986fbfce 100644 --- a/include/mitsuba/render/scenehandler.h +++ b/include/mitsuba/render/scenehandler.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -33,8 +34,20 @@ XERCES_CPP_NAMESPACE_END XERCES_CPP_NAMESPACE_USE MTS_NAMESPACE_BEGIN +/// \brief This exception is thrown when attempting to load an outdated file +class VersionException : public std::runtime_error { +public: + inline VersionException(const std::string &str, const Version &version) + : std::runtime_error(str), m_version(version) { } + + inline const Version &getVersion() const { return m_version; } +private: + Version m_version; +}; + /** - * XML parser for mitsuba scene files. Uses Xerces-C and SAX + * \brief XML parser for Mitsuba scene files. To be used with the + * SAX interface Xerces-C++. */ class MTS_EXPORT_RENDER SceneHandler : public HandlerBase { public: diff --git a/src/bsdfs/bump.cpp b/src/bsdfs/bump.cpp index f962c503..c846e04a 100644 --- a/src/bsdfs/bump.cpp +++ b/src/bsdfs/bump.cpp @@ -32,6 +32,10 @@ MTS_NAMESPACE_BEGIN * \parameter{\Unnamed}{\BSDF}{A BSDF model that should * be affected by the bump map} * } + * \renderings{ + * \rendering{Bump map based on tileable diagonal lines}{bsdf_bump_1} + * \rendering{An irregular bump map}{bsdf_bump_2} + * } * * Bump mapping \cite{Blinn1978Simulation} is a simple technique for cheaply * adding surface detail to a rendering. This is done by perturbing the @@ -41,15 +45,15 @@ MTS_NAMESPACE_BEGIN * without requiring any changes to the input geometry. * * The implementation in Mitsuba uses the common approach of ignoring - * the normally negligible texture-space derivative of the base mesh + * the usually negligible texture-space derivative of the base mesh * surface normal. As side effect of this decision, it is invariant * to constant offsets in the height field texture---only variations in * its luminance cause changes to the shading frame. * * Note that the magnitude of the height field variations influences - * the strength of the displacement. If necessary, the \pluginref{scale} - * texture plugin can be used to magnify the scale of an existing bump map. - * \vspace{1cm} + * the strength of the displacement. If desired, the \pluginref{scale} + * texture plugin can be used to magnify or reduce the effect of a + * bump map texture. * \begin{xml}[caption=A rough metal model with a scaled image-based bump map] * * diff --git a/src/bsdfs/dielectric.cpp b/src/bsdfs/dielectric.cpp index c1617231..179dd3db 100644 --- a/src/bsdfs/dielectric.cpp +++ b/src/bsdfs/dielectric.cpp @@ -39,7 +39,7 @@ MTS_NAMESPACE_BEGIN * \medrendering{Air$\leftrightarrow$Water (IOR: 1.33) interface. * See \lstref{dielectric-water}.}{bsdf_dielectric_water} * \medrendering{Air$\leftrightarrow$Diamond (IOR: 2.419)}{bsdf_dielectric_diamond} - * \medrendering{Air$\leftrightarrow$Glass (IOR: 1.504) interface and absorption within. + * \medrendering{Air$\leftrightarrow$Glass (IOR: 1.504) interface with absorption. * See \lstref{dielectric-glass}.}{bsdf_dielectric_glass} * } * diff --git a/src/bsdfs/phong.cpp b/src/bsdfs/phong.cpp index 8ef18259..766e6e51 100644 --- a/src/bsdfs/phong.cpp +++ b/src/bsdfs/phong.cpp @@ -64,6 +64,7 @@ public: props.getSpectrum("specularReflectance", Spectrum(0.2f))); m_exponent = new ConstantFloatTexture( props.getFloat("exponent", 30.0f)); + m_specularSamplingWeight = 0.0f; } Phong(Stream *stream, InstanceManager *manager) diff --git a/src/bsdfs/plastic.cpp b/src/bsdfs/plastic.cpp index c2fc9522..6ac56b5b 100644 --- a/src/bsdfs/plastic.cpp +++ b/src/bsdfs/plastic.cpp @@ -74,6 +74,7 @@ public: props.getSpectrum("specularReflectance", Spectrum(1.0f))); m_diffuseReflectance = new ConstantSpectrumTexture( props.getSpectrum("diffuseReflectance", Spectrum(0.5f))); + m_specularSamplingWeight = 0.0f; } SmoothPlastic(Stream *stream, InstanceManager *manager) diff --git a/src/bsdfs/roughplastic.cpp b/src/bsdfs/roughplastic.cpp index f900c478..23e076e6 100644 --- a/src/bsdfs/roughplastic.cpp +++ b/src/bsdfs/roughplastic.cpp @@ -62,9 +62,8 @@ MTS_NAMESPACE_BEGIN * factor used to modulate the diffuse reflectance component\default{0.5}} * } * \renderings{ - * \medrendering{Beckmann, $\alpha=0.05$, diffuseReflectance=0}{bsdf_roughplastic_beckmann_lacquer} - * \medrendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann} - * \medrendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx} + * \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann} + * \rendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx} * } * * This plugin implements a realistic microfacet scattering model for rendering @@ -72,6 +71,10 @@ MTS_NAMESPACE_BEGIN * be interpreted as a fancy version of the Cook-Torrance model and should be * preferred over empirical models like \pluginref{phong} and \pluginref{ward} * when possible. + * \renderings{ + * \setcounter{subfigure}{2} + * \rendering{Beckmann, $\alpha=0.05$, diffuseReflectance=0}{bsdf_roughplastic_beckmann_lacquer} + * } * * Microfacet theory describes rough surfaces as an arrangement of unresolved and * ideally specular facets, whose normal directions are given by a specially @@ -100,7 +103,7 @@ MTS_NAMESPACE_BEGIN * the roughness cannot be textured, and anisotropic microfacet * distributions are not allowed. * - * When no parameters are given, the plugin activates the default settings, + * When no parameters are given, the plugin activates the defaults, * which describe a white polypropylene plastic material with a light amount * of roughness modeled using the Beckmann distribution. * @@ -117,8 +120,7 @@ MTS_NAMESPACE_BEGIN * $\alpha$ roughness value into the Phong exponent. * This is done in a way, such that the different * distributions all produce a similar appearance for - * the same value of $\alpha$.\vspace{5mm} - * + * the same value of $\alpha$. * \begin{xml}[caption={A material definition for rough, black laquer.}, label=lst:roughplastic-lacquer] * * @@ -157,6 +159,7 @@ public: m_alpha = m_distribution.transformRoughness( props.getFloat("alpha", 0.1f)); + m_specularSamplingWeight = 0.0f; } RoughPlastic(Stream *stream, InstanceManager *manager) diff --git a/src/bsdfs/ward.cpp b/src/bsdfs/ward.cpp index 66ed9abc..02c28076 100644 --- a/src/bsdfs/ward.cpp +++ b/src/bsdfs/ward.cpp @@ -121,6 +121,7 @@ public: m_alphaV = m_alphaU; else m_alphaV = new ConstantFloatTexture(alphaV); + m_specularSamplingWeight = 0.0f; } Ward(Stream *stream, InstanceManager *manager) diff --git a/src/libcore/plugin.cpp b/src/libcore/plugin.cpp index 0227a4cc..94bb8297 100644 --- a/src/libcore/plugin.cpp +++ b/src/libcore/plugin.cpp @@ -209,5 +209,25 @@ void PluginManager::staticShutdown() { m_instance = NULL; } +Version::Version(const std::string &versionString) { + std::vector tokens = tokenize(trim(versionString), "."); + if (tokens.size() != 3) + SLog(EError, "Unable to parse version string \"%s\"!", versionString.c_str()); + char *end_ptr = NULL; + m_major = strtol(tokens[0].c_str(), &end_ptr, 10); + if (*end_ptr != '\0') + SLog(EError, "Unable to parse the major program version \"%i\"!", tokens[0].c_str()); + m_minor = strtol(tokens[1].c_str(), &end_ptr, 10); + if (*end_ptr != '\0') + SLog(EError, "Unable to parse the minor program version \"%i\"!", tokens[1].c_str()); + m_release = strtol(tokens[2].c_str(), &end_ptr, 10); + if (*end_ptr != '\0') + SLog(EError, "Unable to parse the release program version \"%i\"!", tokens[2].c_str()); +} + +std::string Version::toString() const { + return formatString("%i.%i.%i", m_major, m_minor, m_release); +} + MTS_IMPLEMENT_CLASS(PluginManager, false, Object) MTS_NAMESPACE_END diff --git a/src/libcore/properties.cpp b/src/libcore/properties.cpp index f702b935..90e722e6 100644 --- a/src/libcore/properties.cpp +++ b/src/libcore/properties.cpp @@ -57,7 +57,8 @@ bool Properties::getBoolean(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EBoolean) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_boolean; } @@ -67,7 +68,8 @@ bool Properties::getBoolean(const std::string &name, bool defVal) const { return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EBoolean) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_boolean; } @@ -85,7 +87,8 @@ int Properties::getInteger(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EInteger) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (int) (*it).second.v_long; } @@ -95,7 +98,8 @@ int Properties::getInteger(const std::string &name, int defVal) const { return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EInteger) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (int) (*it).second.v_long; } @@ -113,7 +117,8 @@ int64_t Properties::getLong(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EInteger) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_long; } @@ -123,7 +128,8 @@ int64_t Properties::getLong(const std::string &name, int64_t defVal) const { return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EInteger) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_long; } @@ -133,7 +139,8 @@ size_t Properties::getSize(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EInteger) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); if ((*it).second.v_long < 0) SLog(EError, "Size property \"%s\": expected a nonnegative value!"); (*it).second.queried = true; @@ -145,7 +152,8 @@ size_t Properties::getSize(const std::string &name, size_t defVal) const { return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EInteger) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); if ((*it).second.v_long < 0) SLog(EError, "Size property \"%s\": expected a nonnegative value!"); (*it).second.queried = true; @@ -166,7 +174,8 @@ Float Properties::getFloat(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EFloat) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_float; } @@ -176,7 +185,8 @@ Float Properties::getFloat(const std::string &name, Float defVal) const { return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EFloat) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_float; } @@ -194,7 +204,8 @@ Transform Properties::getTransform(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != ETransform) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_transform; } @@ -204,7 +215,8 @@ Transform Properties::getTransform(const std::string &name, const Transform &def return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != ETransform) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_transform; } @@ -222,7 +234,8 @@ Spectrum Properties::getSpectrum(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != ESpectrum) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_spectrum; } @@ -232,7 +245,8 @@ Spectrum Properties::getSpectrum(const std::string &name, const Spectrum &defVal return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != ESpectrum) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_spectrum; } @@ -250,7 +264,8 @@ std::string Properties::getString(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EString) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_string; } @@ -260,7 +275,8 @@ std::string Properties::getString(const std::string &name, const std::string &de return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EString) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_string; } @@ -286,7 +302,8 @@ Point Properties::getPoint(const std::string &name) const { SLog(EError, "Property \"%s\" missing", name.c_str()); std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EPoint) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_point; } @@ -296,7 +313,8 @@ Point Properties::getPoint(const std::string &name, const Point &defVal) const { return defVal; std::map::const_iterator it = m_elements.find(name); if ((*it).second.type != EPoint) - SLog(EError, "Property \"%s\" has wrong type", name.c_str()); + SLog(EError, "The property \"%s\" has the wrong type (expected ). The detailed " + "listing was:\n%s", name.c_str(), toString().c_str()); (*it).second.queried = true; return (*it).second.v_point; } diff --git a/src/libcore/sched_remote.cpp b/src/libcore/sched_remote.cpp index ac2aa8af..f0a3a0ff 100644 --- a/src/libcore/sched_remote.cpp +++ b/src/libcore/sched_remote.cpp @@ -198,12 +198,12 @@ void RemoteWorker::run() { m_memStream->writeInt(id); m_schedItem.workUnit->save(m_memStream); - if (++m_inFlight == BACKLOG_FACTOR * m_coreCount) { + if (++m_inFlight == MTS_BACKLOG_FACTOR * m_coreCount) { flush(); /* There are now too many packets in transit. Wait until this clears up a bit before attempting to send more work */ - while (m_inFlight > CONTINUE_FACTOR * m_coreCount) + while (m_inFlight > MTS_CONTINUE_FACTOR * m_coreCount) m_finishCond->wait(); } diff --git a/src/librender/bsdf.cpp b/src/librender/bsdf.cpp index a97db56d..9985d736 100644 --- a/src/librender/bsdf.cpp +++ b/src/librender/bsdf.cpp @@ -113,7 +113,7 @@ std::pair BSDF::ensureEnergyConservation( << "happening."; Log(EWarn, "%s", oss.str().c_str()); Properties props("scale"); - props.setFloat("value", scale); + props.setFloat("scale", scale); Texture *scaleTexture1 = static_cast (PluginManager::getInstance()-> createObject(MTS_CLASS(Texture), props)); Texture *scaleTexture2 = static_cast (PluginManager::getInstance()-> diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp index f45146e0..169e3499 100644 --- a/src/librender/scene.cpp +++ b/src/librender/scene.cpp @@ -246,8 +246,10 @@ void Scene::configure() { m_camera->configure(); m_sampler = m_camera->getSampler(); } else { - Log(EWarn, "Unable to set up a default camera -- does the scene " - "contain anything at all?"); + m_camera = static_cast (PluginManager::getInstance()-> + createObject(MTS_CLASS(Camera), props)); + m_camera->configure(); + m_sampler = m_camera->getSampler(); } } diff --git a/src/librender/scenehandler.cpp b/src/librender/scenehandler.cpp index b83220b5..b47ee311 100644 --- a/src/librender/scenehandler.cpp +++ b/src/librender/scenehandler.cpp @@ -127,6 +127,34 @@ void SceneHandler::startElement(const XMLCh* const xmlName, if (name == "transform") m_transform = Transform(); + if (name == "scene") { + std::string versionString = context.attributes["version"]; + if (versionString == "") + throw VersionException(formatString("The requested scene cannot be loaded, since it " + "is missing version information! Since Mitsuba 0.3.0, it is " + "mandatory that scene XML files specify the version of Mitsuba " + "that was used at the time of their creation.\nThis makes it clear " + "how to interpret them in the presence of a changing file format. " + "The version should be specified within the 'scene' tag, " + "e.g.\n\t\n" + "Please update your scene file with the right version number and try reloading it."), + Version()); + Version fileVersion(versionString), currentVersion(MTS_VERSION); + if (!fileVersion.isCompatible(currentVersion)) { + if (fileVersion < currentVersion) { + throw VersionException(formatString("The requested scene is from an older version of Mitsuba " + "(file version: %s, current version: %s), hence the loading process was stopped. " + "Please open the scene from within Mitsuba's graphical user interface (mtsgui) -- " + "it will then be upgraded to the current format.", + fileVersion.toString().c_str(), MTS_VERSION), fileVersion); + } else { + XMLLog(EError, "The requested scene is from an incompatible future version of Mitsuba " + "(file version: %s, current version: %s). Giving up.", + fileVersion.toString().c_str(), MTS_VERSION); + } + } + } + m_context.push(context); } @@ -308,7 +336,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) { else if (intentString == "illuminant") intent = Spectrum::EIlluminant; else - SLog(EError, "Invalid intent \"%s\", must be " + XMLLog(EError, "Invalid intent \"%s\", must be " "\"reflectance\" or \"illuminant\"", intentString.c_str()); } diff --git a/src/qtgui/SConscript b/src/qtgui/SConscript index 596f9394..b99dd5ae 100644 --- a/src/qtgui/SConscript +++ b/src/qtgui/SConscript @@ -22,7 +22,7 @@ def scanFiles(dir, accept=["*.cpp"], reject=[]) : if hasQt: qtEnv = mainEnv.Clone() qtEnv.Append(CPPPATH=['#src/qtgui']) - qtEnv.EnableQt4Modules(['QtGui', 'QtCore', 'QtOpenGL', 'QtXml', 'QtNetwork']) + qtEnv.EnableQt4Modules(['QtGui', 'QtCore', 'QtOpenGL', 'QtXml', 'QtXmlPatterns', 'QtNetwork']) if sys.platform == 'win32': index = qtEnv['CXXFLAGS'].index('_CONSOLE') del qtEnv['CXXFLAGS'][index-1] diff --git a/src/qtgui/common.h b/src/qtgui/common.h index 45c2d72d..fcdc75c3 100644 --- a/src/qtgui/common.h +++ b/src/qtgui/common.h @@ -21,9 +21,11 @@ #include #include +#include #include #include #include +#include using namespace mitsuba; @@ -193,6 +195,7 @@ struct SceneContext { int shownKDTreeLevel; ESelectionMode selectionMode; const Shape *selectedShape; + QDomDocument doc; /* Preview state */ std::deque vpls; @@ -220,44 +223,4 @@ public: } }; -class ProgramVersion { -public: - inline ProgramVersion(const QString &versionString) { - QStringList sl; - sl = versionString.trimmed().split('.'); - SAssert(sl.size() == 3); - major = sl[0].toInt(); - minor = sl[1].toInt(); - release = sl[2].toInt(); - } - - inline bool operator<(const ProgramVersion &other) const { - if (major < other.major) - return true; - if (major > other.major) - return false; - if (minor < other.minor) - return true; - if (minor > other.minor) - return false; - if (release < other.release) - return true; - return false; - } - - inline bool operator==(const ProgramVersion &other) const { - return major == other.major - && minor == other.minor - && release == other.release; - } - - QString toString() const { - return QString("%1.%2.%3").arg(major).arg(minor).arg(release); - } -private: - int major; - int minor; - int release; -}; - #endif // QTGUI_COMMON_H diff --git a/src/qtgui/loaddlg.cpp b/src/qtgui/loaddlg.cpp index e10bac91..4a896d88 100644 --- a/src/qtgui/loaddlg.cpp +++ b/src/qtgui/loaddlg.cpp @@ -68,6 +68,15 @@ void LoadDialog::on_toggleButton_clicked() { } } +void LoadDialog::expand() { + if (!ui->console->isVisible()) { + ui->console->show(); + updateGeometry(); + resize(std::max(width(), 800), 500); + ui->toggleButton->setText("-"); + } +} + void LoadDialog::close() { Logger *logger = Thread::getThread()->getLogger(); logger->setLogLevel(m_oldLogLevel); diff --git a/src/qtgui/loaddlg.h b/src/qtgui/loaddlg.h index de672eb8..bf0c8cf5 100644 --- a/src/qtgui/loaddlg.h +++ b/src/qtgui/loaddlg.h @@ -36,6 +36,7 @@ public: void closeEvent(QCloseEvent *e) { e->ignore(); } + void expand(); protected slots: void onTextMessage(ELogLevel level, const QString &message); void on_toggleButton_clicked(); diff --git a/src/qtgui/mainwindow.cpp b/src/qtgui/mainwindow.cpp index 86a8160d..b2dbed85 100644 --- a/src/qtgui/mainwindow.cpp +++ b/src/qtgui/mainwindow.cpp @@ -30,6 +30,7 @@ #include "updatedlg.h" #include "server.h" #include "save.h" +#include "upgrade.h" #include #include #include @@ -398,9 +399,9 @@ void MainWindow::onNetworkFinished(QNetworkReply *reply) { if (reply->error() == QNetworkReply::NoError) { try { QSettings settings("mitsuba-renderer.org", "qtgui"); - ProgramVersion remote(QString(reply->readAll())); - ProgramVersion ignoredVersion(settings.value("ignoredVersion", "0.0.0").toString()); - ProgramVersion local(MTS_VERSION); + Version remote(QString(reply->readAll()).toStdString()); + Version ignoredVersion(settings.value("ignoredVersion", "0.0.0").toString().toStdString()); + Version local(MTS_VERSION); if (local < remote) { if (!m_manualUpdateCheck && remote == ignoredVersion) @@ -413,7 +414,7 @@ void MainWindow::onNetworkFinished(QNetworkReply *reply) { QMessageBox::information(this, tr("Installed version is current"), QString("

You're up to date!

" "

Mitsuba %1 is still the newest version available.

") - .arg(local.toString()), QMessageBox::Ok); + .arg(local.toString().c_str()), QMessageBox::Ok); } } catch (const std::exception &e) { /* Got something weird and couldn't parse the version string -- @@ -618,14 +619,16 @@ SceneContext *MainWindow::loadScene(const QString &qFileName) { newResolver->addPath(filePath); for (int i=0; iaddPath(m_searchPaths[i].toStdString()); - - ref loadingThread - = new SceneLoader(newResolver, filename.file_string()); LoadDialog *loaddlg = new LoadDialog(this); + SceneContext *result = NULL; + ref loadingThread; loaddlg->setAttribute(Qt::WA_DeleteOnClose); loaddlg->setWindowModality(Qt::ApplicationModal); loaddlg->setWindowTitle("Loading .."); loaddlg->show(); + +retry: + loadingThread = new SceneLoader(newResolver, filename.file_string()); loadingThread->start(); while (loadingThread->isRunning()) { @@ -633,14 +636,55 @@ SceneContext *MainWindow::loadScene(const QString &qFileName) { loadingThread->wait(20); } loadingThread->join(); - loaddlg->close(); - SceneContext *result = loadingThread->getResult(); + result = loadingThread->getResult(); if (result == NULL) { - QMessageBox::critical(this, tr("Unable to load %1").arg(qFileName), - QString(loadingThread->getError().c_str()), - QMessageBox::Ok); + if (loadingThread->isVersionError()) { + Version version = loadingThread->getVersion(); + int ret; + if (version.isValid()) { + ret = QMessageBox::question(this, tr("Version mismatch -- update scene file?"), + QString("The requested scene file is from an older version of Mitsuba " + "(%1). To work with version %2, it will need to be updated. If you " + "continue, Mitsuba will attempt a fully automated upgrade (note that a " + "backup copy will be made).\n\nProceed?") + .arg(version.toString().c_str()) + .arg(MTS_VERSION), QMessageBox::Yes | QMessageBox::Cancel); + } else { + QMessageBox box(QMessageBox::Question, tr("Version mismatch -- update scene file?"), + (loadingThread->getError() + "\n\nAlternatively, if this file is from version 0.2.1" + "(the last release without explicit version numbers), you can perform a fully " + "automated upgrade from this version. A backup copy will be made in this case.").c_str()); + QPushButton *version021Button = box.addButton(tr("Assume version 0.2.1?"), QMessageBox::YesRole); + box.addButton(tr("Cancel"), QMessageBox::RejectRole); + ret = box.exec(); + if (box.clickedButton() == version021Button) { + version = Version(0, 2, 1); + ret = QMessageBox::Yes; + } + } + if (ret == QMessageBox::Yes) { + loaddlg->expand(); + + UpgradeManager upgradeMgr(newResolver); + try { + upgradeMgr.performUpgrade(filename.file_string().c_str(), version); + goto retry; + } catch (const std::exception &ex) { + QMessageBox::critical(this, tr("Unable to update %1").arg(qFileName), + QString(ex.what()), QMessageBox::Ok); + } + } else { + QMessageBox::critical(this, tr("Unable to load %1").arg(qFileName), + QString("No upgrade was performed -- giving up."), QMessageBox::Ok); + } + } else { + QMessageBox::critical(this, tr("Unable to load %1").arg(qFileName), + QString(loadingThread->getError().c_str()), + QMessageBox::Ok); + } } + loaddlg->close(); return result; } diff --git a/src/qtgui/save.cpp b/src/qtgui/save.cpp index 2485b95e..09827fee 100644 --- a/src/qtgui/save.cpp +++ b/src/qtgui/save.cpp @@ -17,7 +17,6 @@ */ #include "save.h" -#include static QDomElement findUniqueChild(QDomElement element, const char *tagName) { QDomElement result; @@ -101,26 +100,7 @@ static void setProperties(QDomDocument &doc, QDomElement &element, } void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { - QDomDocument doc("MitsubaScene"); - QFile file(ctx->fileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox::critical(parent, parent->tr("Unable to save"), - parent->tr("Unable to save changes: could not open the original scene file %1. " - "Was this file moved or deleted in the meantime?").arg(ctx->fileName), - QMessageBox::Ok); - return; - } - if (!doc.setContent(&file)) { - QMessageBox::critical(parent, parent->tr("Unable to save"), - parent->tr("Unable to save changes: could not parse the original scene file %1. " - "Was this file modified in the meantime?").arg(ctx->fileName), - QMessageBox::Ok); - file.close(); - return; - } - file.close(); - - QDomElement root = doc.documentElement(); + QDomElement root = ctx->doc.documentElement(); // ==================================================================== // Serialize the camera configuration @@ -128,7 +108,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement camera = findUniqueChild(root, "camera"); if (camera.isNull()) { - camera = doc.createElement("camera"); + camera = ctx->doc.createElement("camera"); camera.setAttribute("type", "perspective"); root.insertAfter(camera, QDomNode()); } @@ -136,7 +116,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement fovProperty = findProperty(camera, "fov"); if (fovProperty.isNull()) { - fovProperty = doc.createElement("float"); + fovProperty = ctx->doc.createElement("float"); fovProperty.setAttribute("name", "fov"); camera.insertBefore(fovProperty, QDomNode()); } @@ -144,7 +124,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement focusDepthProperty = findProperty(camera, "focusDepth"); if (focusDepthProperty.isNull()) { - focusDepthProperty = doc.createElement("float"); + focusDepthProperty = ctx->doc.createElement("float"); focusDepthProperty.setAttribute("name", "focusDepth"); camera.insertBefore(focusDepthProperty, QDomNode()); } @@ -152,13 +132,13 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement cameraTransform = findUniqueChild(camera, "transform"); if (cameraTransform.isNull()) { - cameraTransform = doc.createElement("transform"); + cameraTransform = ctx->doc.createElement("transform"); cameraTransform.setAttribute("name", "toWorld"); camera.insertBefore(cameraTransform, QDomNode()); } removeChildren(cameraTransform); - QDomElement lookAt = doc.createElement("lookAt"); + QDomElement lookAt = ctx->doc.createElement("lookAt"); cameraTransform.insertAfter(lookAt, QDomNode()); Vector direction = sceneCamera->getInverseViewTransform()(Vector(0,0,1)), @@ -166,7 +146,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { Point t, p = sceneCamera->getInverseViewTransform()(Point(0,0,0)); if (sceneCamera->getViewTransform().det3x3() < 0) { - QDomElement scale = doc.createElement("scale"); + QDomElement scale = ctx->doc.createElement("scale"); scale.setAttribute("x", "-1"); cameraTransform.insertBefore(scale, lookAt); } @@ -182,15 +162,15 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement newSampler, sampler = findUniqueChild(camera, "sampler"); if (sampler.isNull()) { - newSampler = doc.createElement("sampler"); + newSampler = ctx->doc.createElement("sampler"); camera.appendChild(newSampler); } else { - newSampler = doc.createElement("sampler"); + newSampler = ctx->doc.createElement("sampler"); camera.insertAfter(newSampler, sampler); camera.removeChild(sampler); } - setProperties(doc, newSampler, ctx->scene->getSampler()->getProperties()); + setProperties(ctx->doc, newSampler, ctx->scene->getSampler()->getProperties()); // ==================================================================== // Serialize the film configuration @@ -198,7 +178,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement film = findUniqueChild(camera, "film"); if (film.isNull()) { - film = doc.createElement("film"); + film = ctx->doc.createElement("film"); film.setAttribute("type", "exrfilm"); camera.insertAfter(film, QDomNode()); } @@ -207,13 +187,13 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement heightProperty = findProperty(film, "height"); if (widthProperty.isNull()) { - widthProperty = doc.createElement("integer"); + widthProperty = ctx->doc.createElement("integer"); widthProperty.setAttribute("name", "width"); film.insertBefore(widthProperty, QDomNode()); } if (heightProperty.isNull()) { - heightProperty = doc.createElement("integer"); + heightProperty = ctx->doc.createElement("integer"); heightProperty.setAttribute("name", "height"); film.insertBefore(heightProperty, QDomNode()); } @@ -227,14 +207,14 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement gamma = findProperty(film, "gamma"); if (method.isNull()) { - method = doc.createElement("string"); + method = ctx->doc.createElement("string"); method.setAttribute("name", "toneMappingMethod"); film.insertBefore(method, QDomNode()); } method.setAttribute("value", ctx->toneMappingMethod == EReinhard ? "reinhard" : "gamma"); if (gamma.isNull()) { - gamma = doc.createElement("float"); + gamma = ctx->doc.createElement("float"); gamma.setAttribute("name", "gamma"); film.insertBefore(gamma, QDomNode()); } @@ -242,7 +222,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { if (ctx->toneMappingMethod == EGamma) { if (exposure.isNull()) { - exposure = doc.createElement("float"); + exposure = ctx->doc.createElement("float"); exposure.setAttribute("name", "exposure"); film.insertBefore(exposure, QDomNode()); } @@ -253,12 +233,12 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { film.removeChild(reinhardBurn); } else { if (reinhardKey.isNull()) { - reinhardKey = doc.createElement("float"); + reinhardKey = ctx->doc.createElement("float"); reinhardKey.setAttribute("name", "reinhardKey"); film.insertBefore(reinhardKey, QDomNode()); } if (reinhardBurn.isNull()) { - reinhardBurn = doc.createElement("float"); + reinhardBurn = ctx->doc.createElement("float"); reinhardBurn.setAttribute("name", "reinhardBurn"); film.insertBefore(reinhardBurn, QDomNode()); } @@ -279,15 +259,15 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { QDomElement newRFilter, rfilter = findUniqueChild(film, "rfilter"); if (rfilter.isNull()) { - newRFilter = doc.createElement("rfilter"); + newRFilter = ctx->doc.createElement("rfilter"); film.appendChild(rfilter); } else { - newRFilter = doc.createElement("rfilter"); + newRFilter = ctx->doc.createElement("rfilter"); film.insertAfter(newRFilter, rfilter); film.removeChild(rfilter); } - setProperties(doc, newRFilter, ctx->scene->getCamera()-> + setProperties(ctx->doc, newRFilter, ctx->scene->getCamera()-> getFilm()->getReconstructionFilter()->getProperties()); // ==================================================================== @@ -295,16 +275,16 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { // ==================================================================== QDomElement oldIntegratorNode = findUniqueChild(root, "integrator"); - QDomElement newIntegratorNode = doc.createElement("integrator"); + QDomElement newIntegratorNode = ctx->doc.createElement("integrator"); const Integrator *integrator = ctx->scene->getIntegrator(); - setProperties(doc, newIntegratorNode, integrator->getProperties()); + setProperties(ctx->doc, newIntegratorNode, integrator->getProperties()); QDomElement currentIntegratorNode = newIntegratorNode; while (integrator->getSubIntegrator() != NULL) { integrator = integrator->getSubIntegrator(); - QDomElement childIntegratorNode = doc.createElement("integrator"); - setProperties(doc, childIntegratorNode, integrator->getProperties()); + QDomElement childIntegratorNode = ctx->doc.createElement("integrator"); + setProperties(ctx->doc, childIntegratorNode, integrator->getProperties()); currentIntegratorNode.appendChild(childIntegratorNode); currentIntegratorNode = childIntegratorNode; } @@ -312,6 +292,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { root.insertBefore(newIntegratorNode, oldIntegratorNode); root.removeChild(oldIntegratorNode); + QFile file; file.setFileName(targetFile); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { QMessageBox::critical(parent, parent->tr("Unable to save"), @@ -324,9 +305,14 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { is more suitable for human consumption .. Beware: the code below is tailored to Qt's output and won't work on arbitrary XML files */ - QString textContent = doc.toString(); + QString textContent = ctx->doc.toString(); QTextStream input(&textContent); QTextStream output(&file); + cleanupXML(input, output); + file.close(); +} + +void cleanupXML(QTextStream &input, QTextStream &output) { QRegExp filenameRegExp("filename=\"[^\"]*\""), nameRegExp("name=\"[^\"]*\""), @@ -362,10 +348,15 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { int nameMatch = nameRegExp.indexIn(line), filenameMatch = filenameRegExp.indexIn(line), nameLength = nameRegExp.matchedLength(); + if (tagMatch != -1 && nameMatch != -1 && filenameMatch == -1) { - line = line.left(tagLength) + line.mid(nameMatch, nameLength) + " " - + line.mid(tagMatch+tagLength, nameMatch-(tagMatch+tagLength)) - + line.mid(nameMatch+nameLength); + QString a = line.mid(tagLength, nameMatch-tagLength).trimmed(), + b = line.mid(nameMatch+nameLength).trimmed(); + line = line.left(tagLength) + line.mid(nameMatch, nameLength); + if (a.length() > 0) + line += " " + a; + if (b.length() > 0) + line += " " + b; } /* Add an extra newline if this is an object tag, and if there @@ -414,6 +405,8 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { hasContents = true; } else if (line.endsWith(" >")) { line = line.left(line.size()-2) + QString(">"); + } else if (line.endsWith("?>")) { + hasContents = true; } if (closeTag.indexIn(line) == 0) @@ -421,6 +414,4 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { output << line << endl; } - file.close(); } - diff --git a/src/qtgui/save.h b/src/qtgui/save.h index 35e4043d..3be6eae2 100644 --- a/src/qtgui/save.h +++ b/src/qtgui/save.h @@ -19,3 +19,4 @@ #include "common.h" extern void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile); +extern void cleanupXML(QTextStream &input, QTextStream &output); diff --git a/src/qtgui/sceneloader.cpp b/src/qtgui/sceneloader.cpp index bae198b0..610121c9 100644 --- a/src/qtgui/sceneloader.cpp +++ b/src/qtgui/sceneloader.cpp @@ -27,6 +27,7 @@ SceneLoader::SceneLoader(FileResolver *resolver, const std::string &filename) : Thread("load"), m_resolver(resolver), m_filename(filename) { m_wait = new WaitFlag(); + m_versionError = false; } SceneLoader::~SceneLoader() { @@ -35,9 +36,7 @@ SceneLoader::~SceneLoader() { void SceneLoader::run() { Thread::getThread()->setFileResolver(m_resolver); SAXParser* parser = new SAXParser(); - std::string lowerCase = m_filename; - for(size_t i=0; iparse(m_filename.c_str()); + + try { + parser->parse(m_filename.c_str()); + } catch (const VersionException &ex) { + m_versionError = true; + m_version = ex.getVersion(); + throw; + } + ref scene = handler->getScene(); scene->setSourceFile(m_filename); @@ -105,10 +112,19 @@ void SceneLoader::run() { SLog(EError, "Unable to load scene: no film found!"); if (scene->getLuminaires().size() == 0) SLog(EError, "Unable to load scene: no light sources found!"); - Vector2i size = scene->getFilm()->getSize(); Camera *camera = scene->getCamera(); + /* Also generate a DOM representation for the Qt-based GUI */ + QFile file(m_filename.c_str()); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + Log(EError, "Unable to open the file \"%s\"", m_filename.c_str()); + QString errorMsg; + int line, column; + if (!m_result->doc.setContent(&file, &errorMsg, &line, &column)) + SLog(EError, "Unable to parse file: error %s at line %i, colum %i", + errorMsg.toStdString().c_str(), line, column); + m_result->scene = scene; m_result->sceneResID = Scheduler::getInstance()->registerResource(scene); m_result->renderJob = NULL; diff --git a/src/qtgui/sceneloader.h b/src/qtgui/sceneloader.h index 498105e8..68af16a2 100644 --- a/src/qtgui/sceneloader.h +++ b/src/qtgui/sceneloader.h @@ -35,6 +35,8 @@ public: inline SceneContext *getResult() { return m_result; } inline const std::string &getError() const { return m_error; } + inline bool isVersionError() const { return m_versionError; } + inline const Version &getVersion() const { return m_version; } protected: virtual ~SceneLoader(); private: @@ -42,6 +44,8 @@ private: ref m_wait; SceneContext *m_result; std::string m_error, m_filename; + bool m_versionError; + Version m_version; }; #endif // __SCENELOADER_H diff --git a/src/qtgui/updatedlg.cpp b/src/qtgui/updatedlg.cpp index 6e8df666..b0032329 100644 --- a/src/qtgui/updatedlg.cpp +++ b/src/qtgui/updatedlg.cpp @@ -19,19 +19,19 @@ #include "ui_updatedlg.h" #include "updatedlg.h" -UpdateDialog::UpdateDialog(QWidget *parent, const ProgramVersion &local, - const ProgramVersion &remote) : QDialog(parent), +UpdateDialog::UpdateDialog(QWidget *parent, const Version &local, + const Version &remote) : QDialog(parent), ui(new Ui::UpdateDialog) { ui->setupUi(this); + m_remoteVersion = remote.toString().c_str(); ui->versionLabel->setText(QApplication::translate("UpdateDialog", "Version %1 has been released (you are using %2). Would you like to visit the download page?", - 0, QApplication::UnicodeUTF8).arg(remote.toString()).arg(local.toString())); + 0, QApplication::UnicodeUTF8).arg(m_remoteVersion).arg(local.toString().c_str())); ui->changeView->setHtml("Loading change log .."); m_networkManager = new QNetworkAccessManager(this); connect(m_networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onNetworkFinished(QNetworkReply *))); m_networkReply = m_networkManager->get(QNetworkRequest(QUrl("http://www.mitsuba-renderer.org/changelog.html"))); - m_remoteVersion = remote.toString(); } void UpdateDialog::onNetworkFinished(QNetworkReply *reply) { diff --git a/src/qtgui/updatedlg.h b/src/qtgui/updatedlg.h index d1e7bc3b..57185c9e 100644 --- a/src/qtgui/updatedlg.h +++ b/src/qtgui/updatedlg.h @@ -29,8 +29,8 @@ namespace Ui { class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget *parent, const ProgramVersion &local, - const ProgramVersion &remote); + UpdateDialog(QWidget *parent, const Version &local, + const Version &remote); ~UpdateDialog(); protected slots: void on_skipButton_clicked(); diff --git a/src/qtgui/upgrade.cpp b/src/qtgui/upgrade.cpp new file mode 100644 index 00000000..15142ecd --- /dev/null +++ b/src/qtgui/upgrade.cpp @@ -0,0 +1,166 @@ +/* + This file is part of Mitsuba, a physically based rendering system. + + Copyright (c) 2007-2011 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 "upgrade.h" +#include "save.h" +#include +#include +#include + +struct VersionComparator { + inline bool operator()(const std::pair &s1, + const std::pair &s2) const { + return s1.first < s2.first; + } +}; + +class XSLTMessageHandler : public QAbstractMessageHandler { +public: + XSLTMessageHandler() : m_fatalError(false) { } + + void handleMessage(QtMsgType type, const QString &_descr, const QUrl &id, const QSourceLocation &loc) { + QString descr(_descr); + int paragraphStart = descr.indexOf("

"); + int paragraphEnd = descr.indexOf("

"); + if (paragraphStart != -1 && paragraphEnd != -1) + descr = descr.mid(paragraphStart+3, paragraphEnd-paragraphStart-3); + + if (loc.isNull()) + SLog(EWarn, "%s", descr.toStdString().c_str()); + else + SLog(EWarn, "%s (line %i, column %i, url=%s)", descr.toStdString().c_str(), + loc.line(), loc.column(), id.toString().toStdString().c_str()); + + if (type == QtFatalMsg) + m_fatalError = true; + } + + inline bool fatalError() const { return m_fatalError; } +private: + bool m_fatalError; +}; + +UpgradeManager::UpgradeManager(const FileResolver *resolver) : m_resolver(resolver){ + fs::path transformationPath = + resolver->resolveAbsolute("data/schema/scene.xsd").parent_path(); + + fs::directory_iterator it(transformationPath), end; + SLog(EInfo, "Searching for transformations.."); + + for (; it != end; ++it) { + fs::path file = *it; + if (boost::to_lower_copy(file.extension()) != ".xsl" || + !boost::starts_with(file.filename(), "upgrade_")) + continue; + std::string filename = file.filename(); + Version version(filename.substr(8, filename.length()-12)); + m_transformations.push_back(std::make_pair(version, file)); + } + + std::sort(m_transformations.begin(), m_transformations.end(), VersionComparator()); + + for (size_t i=0; i" + << endl << endl; + cleanupXML(input, output); + file.close(); +} diff --git a/src/qtgui/upgrade.h b/src/qtgui/upgrade.h new file mode 100644 index 00000000..aa1bd06c --- /dev/null +++ b/src/qtgui/upgrade.h @@ -0,0 +1,35 @@ +/* + This file is part of Mitsuba, a physically based rendering system. + + Copyright (c) 2007-2011 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 . +*/ + +#if !defined(__UPGRADEMGR_H) +#define __UPGRADEMGR_H + +#include "common.h" + +class UpgradeManager : public QObject { + Q_OBJECT +public: + UpgradeManager(const FileResolver *resolver); + + void performUpgrade(const QString &path, const Version &version); +private: + const FileResolver *m_resolver; + std::vector > m_transformations; +}; + +#endif // __UPGRADEMGR_H diff --git a/src/textures/scale.cpp b/src/textures/scale.cpp index 885315fa..e95fd9eb 100644 --- a/src/textures/scale.cpp +++ b/src/textures/scale.cpp @@ -18,8 +18,7 @@ #include #include -#include -#include +#include MTS_NAMESPACE_BEGIN @@ -31,6 +30,10 @@ MTS_NAMESPACE_BEGIN class ScalingTexture : public Texture { public: ScalingTexture(const Properties &props) : Texture(props) { + if (props.hasProperty("value")) + m_nested = new ConstantSpectrumTexture( + props.getSpectrum("value")); + if (props.hasProperty("scale") && props.getType("scale") == Properties::EFloat) m_scale = Spectrum(props.getFloat("scale", 1.0f)); else @@ -49,11 +52,10 @@ public: } void addChild(const std::string &name, ConfigurableObject *child) { - if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) { + if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) m_nested = static_cast(child); - } else { + else Texture::addChild(name, child); - } } Spectrum getValue(const Intersection &its) const {