Added an XSLT-based scene 'upgrade' tool to handle content from old versions of Mitsuba.

Some of the documentation was improved, and there are now example bump map
renderings.
metadata
Wenzel Jakob 2011-07-17 00:28:28 +02:00
parent 17eb2166f0
commit ef2c813b10
37 changed files with 796 additions and 171 deletions

View File

@ -201,7 +201,7 @@ env.Replace(CPPFLAGS=cppFlagsPrevious)
env.Replace(CXXFLAGS=cxxFlagsPrevious) env.Replace(CXXFLAGS=cxxFlagsPrevious)
sys.stdout.write("Checking for Mitsuba version .. ") 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="" MTS_VERSION=""
for line in file: for line in file:
if line.startswith("#define MTS_VERSION "): if line.startswith("#define MTS_VERSION "):

View File

@ -79,7 +79,7 @@ if sys.platform == 'win32':
if hasQt: if hasQt:
install('#dist', ['qtgui/mtsgui.exe']) install('#dist', ['qtgui/mtsgui.exe'])
install('#dist', ['QtCore4.dll', 'QtGui4.dll', 'QtXml4.dll', 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': elif sys.platform == 'darwin':
for i in plugins: for i in plugins:
installTargets += env.Install('#Mitsuba.app/plugins', i) 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') 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') 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') 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') 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') 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(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(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(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') installTargets += env.AddPostAction(opengl, 'install_name_tool -change QtCore.framework/Versions/4/QtCore @executable_path/../Frameworks/QtCore $TARGET')

View File

@ -0,0 +1,198 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- Stylesheet to upgrade pre-0.3.0 scenes -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:preserve-space elements="*"/>
<xsl:template match="scene">
<scene>
<xsl:attribute name="version">0.3.0</xsl:attribute>
<xsl:apply-templates select="node()"/>
</scene>
</xsl:template>
<!-- Replace the old version of the lookAt tag -->
<xsl:template match="lookAt[@ox]">
<scale x="-1"/>
<lookAt>
<xsl:attribute name="origin">
<xsl:value-of select="@ox"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@oy"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@oz"/>
</xsl:attribute>
<xsl:attribute name="target">
<xsl:value-of select="@tx"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@ty"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@tz"/>
</xsl:attribute>
<xsl:if test="@ux">
<xsl:attribute name="up">
<xsl:value-of select="@ux"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@uy"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@uz"/>
</xsl:attribute>
</xsl:if>
</lookAt>
</xsl:template>
<!-- There are no more 'diffuseAmount' or 'specularAmount' parameters in
the microfacet/phong/ward plugins, and the default values have changed -->
<xsl:template match="bsdf[@type='microfacet' or @type='phong' or @type='ward']">
<xsl:variable name="diffuseAmount">
<xsl:choose>
<xsl:when test="float[@name='diffuseAmount']">
<xsl:value-of select="float[@name='diffuseAmount']/@value"/>
</xsl:when>
<xsl:otherwise>1.0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="specularAmount">
<xsl:choose>
<xsl:when test="float[@name='specularAmount']">
<xsl:value-of select="float[@name='specularAmount']/@value"/>
</xsl:when>
<xsl:otherwise>1.0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="specularReflectance">
<xsl:choose>
<xsl:when test="@type='microfacet'">1.0</xsl:when>
<xsl:when test="@type='phong'">0.2</xsl:when>
<xsl:when test="@type='ward'">0.2</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="diffuseReflectance">
<xsl:choose>
<xsl:when test="@type='microfacet'">0.0</xsl:when>
<xsl:when test="@type='phong'">0.5</xsl:when>
<xsl:when test="@type='ward'">0.5</xsl:when>
</xsl:choose>
</xsl:variable>
<bsdf>
<xsl:apply-templates select="@*"/>
<xsl:choose>
<xsl:when test="node()[@name='diffuseReflectance']">
<xsl:apply-templates select="node()[@name='diffuseReflectance']" mode="scaled">
<xsl:with-param name="scale" select="$diffuseAmount"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<spectrum name="diffuseReflectance" value="{number($diffuseAmount)*number($diffuseReflectance)}"/>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="node()[@name='specularReflectance']">
<xsl:apply-templates select="node()[@name='specularReflectance']" mode="scaled">
<xsl:with-param name="scale" select="$specularAmount"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<spectrum name="specularReflectance" value="{number($specularAmount)*number($specularReflectance)}"/>
</xsl:otherwise>
</xsl:choose>
</bsdf>
</xsl:template>
<xsl:template match="ref|texture|rgb|srgb|spectrum|blackbody" mode="scaled">
<xsl:param name="scale"/>
<xsl:choose>
<xsl:when test="number($scale)!=1">
<texture type="scale">
<xsl:if test="@name">
<xsl:attribute name="name">
<xsl:value-of select="@name"/>
</xsl:attribute>
</xsl:if>
<float name="scale" value="{$scale}"/>
<xsl:choose>
<xsl:when test="texture|ref">
<xsl:copy>
<xsl:attribute name="name">value</xsl:attribute>
<xsl:apply-templates select="@*[local-name() != 'name']|node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:attribute name="name">value</xsl:attribute>
<xsl:apply-templates select="@*[local-name() != 'name']|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</texture>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Update the name of the lambertian plugin -->
<xsl:template match="bsdf[@type='lambertian']/@type">
<xsl:attribute name="type">diffuse</xsl:attribute>
</xsl:template>
<!-- Update the name of the microfacet plugin -->
<xsl:template match="bsdf[@type='microfacet']/@type">
<xsl:attribute name="type">roughplastic</xsl:attribute>
</xsl:template>
<xsl:template match="bsdf[@type='microfacet']/float[@name='alphaB']/@name">
<xsl:attribute name="name">alpha</xsl:attribute>
</xsl:template>
<!-- There is no more 'mirror' plugin; replace with smooth chrome -->
<xsl:template match="bsdf[@type='mirror']/@type">
<xsl:attribute name="type">conductor</xsl:attribute>
</xsl:template>
<xsl:template match="bsdf[@type='mirror']">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<string name="material" value="Cr"/>
</xsl:copy>
</xsl:template>
<!-- Update the name of the roughmetal plugin -->
<xsl:template match="bsdf[@type='roughmetal']/@type">
<xsl:attribute name="type">roughconductor</xsl:attribute>
</xsl:template>
<xsl:template match="bsdf[@type='roughmetal']/float[@name='alphaB']/@name">
<xsl:attribute name="name">alpha</xsl:attribute>
</xsl:template>
<xsl:template match="bsdf[@type='roughmetal']/float[@name='ior']/@name">
<xsl:attribute name="name">eta</xsl:attribute>
</xsl:template>
<!-- Update the name of the composite plugin -->
<xsl:template match="bsdf[@type='composite']/@type">
<xsl:attribute name="type">mixture</xsl:attribute>
</xsl:template>
<!-- Update the name of the exrtexture plugin -->
<xsl:template match="texture[@type='exrtexture']/@type">
<xsl:attribute name="type">bitmap</xsl:attribute>
</xsl:template>
<!-- Update the name of the ldrtexture plugin -->
<xsl:template match="texture[@type='ldrtexture']/@type">
<xsl:attribute name="type">bitmap</xsl:attribute>
</xsl:template>
<!-- Default copy rule -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

BIN
doc/images/bsdf_bump_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
doc/images/bsdf_bump_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

View File

@ -15,7 +15,7 @@
\usepackage{color} \usepackage{color}
\usepackage{colortbl} \usepackage{colortbl}
\usepackage{listings} \usepackage{listings}
\usepackage{subfigure} \usepackage[hang]{subfigure}
\usepackage{enumerate} \usepackage{enumerate}
\usepackage{ragged2e} % Ragged-right columns with hyphenation \usepackage{ragged2e} % Ragged-right columns with hyphenation
\usepackage{macros} \usepackage{macros}

View File

@ -53,6 +53,7 @@ class Plugin;
class PluginManager; class PluginManager;
class ProgressReporter; class ProgressReporter;
class Properties; class Properties;
struct Version;
class Random; class Random;
struct Ray; struct Ray;
struct RayDifferential; struct RayDifferential;

View File

@ -21,14 +21,17 @@
#include <mitsuba/core/sched.h> #include <mitsuba/core/sched.h>
/// Default port of <tt>mtssrv</tt>
#define MTS_DEFAULT_PORT 7554
/** How many work units should be sent to a remote worker /** How many work units should be sent to a remote worker
at a time? This is a multiple of the worker's core count */ 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 /** Once the back log factor drops below this value (also a
multiple of the core size), the stream processor will multiple of the core size), the stream processor will
continue sending batches of work units */ continue sending batches of work units */
#define CONTINUE_FACTOR 2 #define MTS_CONTINUE_FACTOR 2
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 */

View File

@ -34,16 +34,6 @@
#include <stdexcept> #include <stdexcept>
#include <limits> #include <limits>
/// 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 <tt>mtssrv</tt>
#define MTS_DEFAULT_PORT 7554
using std::cout; using std::cout;
using std::cerr; using std::cerr;
using std::endl; using std::endl;
@ -51,6 +41,7 @@ using std::endl;
/** /**
* Include a basic subset of the core classes * Include a basic subset of the core classes
*/ */
#include <mitsuba/core/version.h>
#include <mitsuba/core/constants.h> #include <mitsuba/core/constants.h>
#include <mitsuba/core/fwd.h> #include <mitsuba/core/fwd.h>
#include <mitsuba/render/fwd.h> #include <mitsuba/render/fwd.h>

View File

@ -23,6 +23,7 @@
#include <xercesc/sax/AttributeList.hpp> #include <xercesc/sax/AttributeList.hpp>
#include <mitsuba/core/plugin.h> #include <mitsuba/core/plugin.h>
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
#include <mitsuba/core/version.h>
#include <stack> #include <stack>
#include <map> #include <map>
@ -33,8 +34,20 @@ XERCES_CPP_NAMESPACE_END
XERCES_CPP_NAMESPACE_USE XERCES_CPP_NAMESPACE_USE
MTS_NAMESPACE_BEGIN 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 { class MTS_EXPORT_RENDER SceneHandler : public HandlerBase {
public: public:

View File

@ -32,6 +32,10 @@ MTS_NAMESPACE_BEGIN
* \parameter{\Unnamed}{\BSDF}{A BSDF model that should * \parameter{\Unnamed}{\BSDF}{A BSDF model that should
* be affected by the bump map} * 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 * Bump mapping \cite{Blinn1978Simulation} is a simple technique for cheaply
* adding surface detail to a rendering. This is done by perturbing the * 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. * without requiring any changes to the input geometry.
* *
* The implementation in Mitsuba uses the common approach of ignoring * 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 * surface normal. As side effect of this decision, it is invariant
* to constant offsets in the height field texture---only variations in * to constant offsets in the height field texture---only variations in
* its luminance cause changes to the shading frame. * its luminance cause changes to the shading frame.
* *
* Note that the magnitude of the height field variations influences * Note that the magnitude of the height field variations influences
* the strength of the displacement. If necessary, the \pluginref{scale} * the strength of the displacement. If desired, the \pluginref{scale}
* texture plugin can be used to magnify the scale of an existing bump map. * texture plugin can be used to magnify or reduce the effect of a
* \vspace{1cm} * bump map texture.
* \begin{xml}[caption=A rough metal model with a scaled image-based bump map] * \begin{xml}[caption=A rough metal model with a scaled image-based bump map]
* <bsdf type="bump"> * <bsdf type="bump">
* <!-- The bump map is applied to a rough metal BRDF --> * <!-- The bump map is applied to a rough metal BRDF -->

View File

@ -39,7 +39,7 @@ MTS_NAMESPACE_BEGIN
* \medrendering{Air$\leftrightarrow$Water (IOR: 1.33) interface. * \medrendering{Air$\leftrightarrow$Water (IOR: 1.33) interface.
* See \lstref{dielectric-water}.}{bsdf_dielectric_water} * See \lstref{dielectric-water}.}{bsdf_dielectric_water}
* \medrendering{Air$\leftrightarrow$Diamond (IOR: 2.419)}{bsdf_dielectric_diamond} * \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} * See \lstref{dielectric-glass}.}{bsdf_dielectric_glass}
* } * }
* *

View File

@ -64,6 +64,7 @@ public:
props.getSpectrum("specularReflectance", Spectrum(0.2f))); props.getSpectrum("specularReflectance", Spectrum(0.2f)));
m_exponent = new ConstantFloatTexture( m_exponent = new ConstantFloatTexture(
props.getFloat("exponent", 30.0f)); props.getFloat("exponent", 30.0f));
m_specularSamplingWeight = 0.0f;
} }
Phong(Stream *stream, InstanceManager *manager) Phong(Stream *stream, InstanceManager *manager)

View File

@ -74,6 +74,7 @@ public:
props.getSpectrum("specularReflectance", Spectrum(1.0f))); props.getSpectrum("specularReflectance", Spectrum(1.0f)));
m_diffuseReflectance = new ConstantSpectrumTexture( m_diffuseReflectance = new ConstantSpectrumTexture(
props.getSpectrum("diffuseReflectance", Spectrum(0.5f))); props.getSpectrum("diffuseReflectance", Spectrum(0.5f)));
m_specularSamplingWeight = 0.0f;
} }
SmoothPlastic(Stream *stream, InstanceManager *manager) SmoothPlastic(Stream *stream, InstanceManager *manager)

View File

@ -62,9 +62,8 @@ MTS_NAMESPACE_BEGIN
* factor used to modulate the diffuse reflectance component\default{0.5}} * factor used to modulate the diffuse reflectance component\default{0.5}}
* } * }
* \renderings{ * \renderings{
* \medrendering{Beckmann, $\alpha=0.05$, diffuseReflectance=0}{bsdf_roughplastic_beckmann_lacquer} * \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann}
* \medrendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann} * \rendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx}
* \medrendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx}
* } * }
* *
* This plugin implements a realistic microfacet scattering model for rendering * 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 * be interpreted as a fancy version of the Cook-Torrance model and should be
* preferred over empirical models like \pluginref{phong} and \pluginref{ward} * preferred over empirical models like \pluginref{phong} and \pluginref{ward}
* when possible. * 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 * Microfacet theory describes rough surfaces as an arrangement of unresolved and
* ideally specular facets, whose normal directions are given by a specially * 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 * the roughness cannot be textured, and anisotropic microfacet
* distributions are not allowed. * 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 * which describe a white polypropylene plastic material with a light amount
* of roughness modeled using the Beckmann distribution. * of roughness modeled using the Beckmann distribution.
* *
@ -117,8 +120,7 @@ MTS_NAMESPACE_BEGIN
* $\alpha$ roughness value into the Phong exponent. * $\alpha$ roughness value into the Phong exponent.
* This is done in a way, such that the different * This is done in a way, such that the different
* distributions all produce a similar appearance for * 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] * \begin{xml}[caption={A material definition for rough, black laquer.}, label=lst:roughplastic-lacquer]
* <bsdf type="roughplastic"> * <bsdf type="roughplastic">
* <string name="distribution" value="beckmann"/> * <string name="distribution" value="beckmann"/>
@ -157,6 +159,7 @@ public:
m_alpha = m_distribution.transformRoughness( m_alpha = m_distribution.transformRoughness(
props.getFloat("alpha", 0.1f)); props.getFloat("alpha", 0.1f));
m_specularSamplingWeight = 0.0f;
} }
RoughPlastic(Stream *stream, InstanceManager *manager) RoughPlastic(Stream *stream, InstanceManager *manager)

View File

@ -121,6 +121,7 @@ public:
m_alphaV = m_alphaU; m_alphaV = m_alphaU;
else else
m_alphaV = new ConstantFloatTexture(alphaV); m_alphaV = new ConstantFloatTexture(alphaV);
m_specularSamplingWeight = 0.0f;
} }
Ward(Stream *stream, InstanceManager *manager) Ward(Stream *stream, InstanceManager *manager)

View File

@ -209,5 +209,25 @@ void PluginManager::staticShutdown() {
m_instance = NULL; m_instance = NULL;
} }
Version::Version(const std::string &versionString) {
std::vector<std::string> 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_IMPLEMENT_CLASS(PluginManager, false, Object)
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -57,7 +57,8 @@ bool Properties::getBoolean(const std::string &name) const {
SLog(EError, "Property \"%s\" missing", name.c_str()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EBoolean) 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 <boolean>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_boolean; return (*it).second.v_boolean;
} }
@ -67,7 +68,8 @@ bool Properties::getBoolean(const std::string &name, bool defVal) const {
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EBoolean) 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 <boolean>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_boolean; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EInteger) 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 <integer>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (int) (*it).second.v_long; return (int) (*it).second.v_long;
} }
@ -95,7 +98,8 @@ int Properties::getInteger(const std::string &name, int defVal) const {
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EInteger) 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 <integer>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (int) (*it).second.v_long; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EInteger) 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 <integer>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_long; return (*it).second.v_long;
} }
@ -123,7 +128,8 @@ int64_t Properties::getLong(const std::string &name, int64_t defVal) const {
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EInteger) 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 <integer>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_long; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EInteger) 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 <integer>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
if ((*it).second.v_long < 0) if ((*it).second.v_long < 0)
SLog(EError, "Size property \"%s\": expected a nonnegative value!"); SLog(EError, "Size property \"%s\": expected a nonnegative value!");
(*it).second.queried = true; (*it).second.queried = true;
@ -145,7 +152,8 @@ size_t Properties::getSize(const std::string &name, size_t defVal) const {
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EInteger) 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 <integer>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
if ((*it).second.v_long < 0) if ((*it).second.v_long < 0)
SLog(EError, "Size property \"%s\": expected a nonnegative value!"); SLog(EError, "Size property \"%s\": expected a nonnegative value!");
(*it).second.queried = true; (*it).second.queried = true;
@ -166,7 +174,8 @@ Float Properties::getFloat(const std::string &name) const {
SLog(EError, "Property \"%s\" missing", name.c_str()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EFloat) 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 <float>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_float; return (*it).second.v_float;
} }
@ -176,7 +185,8 @@ Float Properties::getFloat(const std::string &name, Float defVal) const {
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EFloat) 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 <float>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_float; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != ETransform) 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 <transform>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_transform; return (*it).second.v_transform;
} }
@ -204,7 +215,8 @@ Transform Properties::getTransform(const std::string &name, const Transform &def
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != ETransform) 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 <transform>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_transform; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != ESpectrum) 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 <spectrum>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_spectrum; return (*it).second.v_spectrum;
} }
@ -232,7 +245,8 @@ Spectrum Properties::getSpectrum(const std::string &name, const Spectrum &defVal
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != ESpectrum) 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 <spectrum>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_spectrum; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EString) 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 <string>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_string; return (*it).second.v_string;
} }
@ -260,7 +275,8 @@ std::string Properties::getString(const std::string &name, const std::string &de
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EString) 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 <string>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_string; 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()); SLog(EError, "Property \"%s\" missing", name.c_str());
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EPoint) 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 <point>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_point; return (*it).second.v_point;
} }
@ -296,7 +313,8 @@ Point Properties::getPoint(const std::string &name, const Point &defVal) const {
return defVal; return defVal;
std::map<std::string, Element>::const_iterator it = m_elements.find(name); std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if ((*it).second.type != EPoint) 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 <point>). The detailed "
"listing was:\n%s", name.c_str(), toString().c_str());
(*it).second.queried = true; (*it).second.queried = true;
return (*it).second.v_point; return (*it).second.v_point;
} }

View File

@ -198,12 +198,12 @@ void RemoteWorker::run() {
m_memStream->writeInt(id); m_memStream->writeInt(id);
m_schedItem.workUnit->save(m_memStream); m_schedItem.workUnit->save(m_memStream);
if (++m_inFlight == BACKLOG_FACTOR * m_coreCount) { if (++m_inFlight == MTS_BACKLOG_FACTOR * m_coreCount) {
flush(); flush();
/* There are now too many packets in transit. Wait /* There are now too many packets in transit. Wait
until this clears up a bit before attempting to until this clears up a bit before attempting to
send more work */ send more work */
while (m_inFlight > CONTINUE_FACTOR * m_coreCount) while (m_inFlight > MTS_CONTINUE_FACTOR * m_coreCount)
m_finishCond->wait(); m_finishCond->wait();
} }

View File

@ -113,7 +113,7 @@ std::pair<Texture *, Texture *> BSDF::ensureEnergyConservation(
<< "happening."; << "happening.";
Log(EWarn, "%s", oss.str().c_str()); Log(EWarn, "%s", oss.str().c_str());
Properties props("scale"); Properties props("scale");
props.setFloat("value", scale); props.setFloat("scale", scale);
Texture *scaleTexture1 = static_cast<Texture *> (PluginManager::getInstance()-> Texture *scaleTexture1 = static_cast<Texture *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Texture), props)); createObject(MTS_CLASS(Texture), props));
Texture *scaleTexture2 = static_cast<Texture *> (PluginManager::getInstance()-> Texture *scaleTexture2 = static_cast<Texture *> (PluginManager::getInstance()->

View File

@ -246,8 +246,10 @@ void Scene::configure() {
m_camera->configure(); m_camera->configure();
m_sampler = m_camera->getSampler(); m_sampler = m_camera->getSampler();
} else { } else {
Log(EWarn, "Unable to set up a default camera -- does the scene " m_camera = static_cast<Camera *> (PluginManager::getInstance()->
"contain anything at all?"); createObject(MTS_CLASS(Camera), props));
m_camera->configure();
m_sampler = m_camera->getSampler();
} }
} }

View File

@ -127,6 +127,34 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
if (name == "transform") if (name == "transform")
m_transform = 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<scene version=\"" MTS_VERSION "\">\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); m_context.push(context);
} }
@ -308,7 +336,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
else if (intentString == "illuminant") else if (intentString == "illuminant")
intent = Spectrum::EIlluminant; intent = Spectrum::EIlluminant;
else else
SLog(EError, "Invalid intent \"%s\", must be " XMLLog(EError, "Invalid intent \"%s\", must be "
"\"reflectance\" or \"illuminant\"", intentString.c_str()); "\"reflectance\" or \"illuminant\"", intentString.c_str());
} }

View File

@ -22,7 +22,7 @@ def scanFiles(dir, accept=["*.cpp"], reject=[]) :
if hasQt: if hasQt:
qtEnv = mainEnv.Clone() qtEnv = mainEnv.Clone()
qtEnv.Append(CPPPATH=['#src/qtgui']) qtEnv.Append(CPPPATH=['#src/qtgui'])
qtEnv.EnableQt4Modules(['QtGui', 'QtCore', 'QtOpenGL', 'QtXml', 'QtNetwork']) qtEnv.EnableQt4Modules(['QtGui', 'QtCore', 'QtOpenGL', 'QtXml', 'QtXmlPatterns', 'QtNetwork'])
if sys.platform == 'win32': if sys.platform == 'win32':
index = qtEnv['CXXFLAGS'].index('_CONSOLE') index = qtEnv['CXXFLAGS'].index('_CONSOLE')
del qtEnv['CXXFLAGS'][index-1] del qtEnv['CXXFLAGS'][index-1]

View File

@ -21,9 +21,11 @@
#include <mitsuba/core/platform.h> #include <mitsuba/core/platform.h>
#include <QtGui> #include <QtGui>
#include <QtXml>
#include <mitsuba/render/scene.h> #include <mitsuba/render/scene.h>
#include <mitsuba/render/vpl.h> #include <mitsuba/render/vpl.h>
#include <mitsuba/core/bitmap.h> #include <mitsuba/core/bitmap.h>
#include <mitsuba/core/version.h>
using namespace mitsuba; using namespace mitsuba;
@ -193,6 +195,7 @@ struct SceneContext {
int shownKDTreeLevel; int shownKDTreeLevel;
ESelectionMode selectionMode; ESelectionMode selectionMode;
const Shape *selectedShape; const Shape *selectedShape;
QDomDocument doc;
/* Preview state */ /* Preview state */
std::deque<VPL> vpls; std::deque<VPL> 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 #endif // QTGUI_COMMON_H

View File

@ -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() { void LoadDialog::close() {
Logger *logger = Thread::getThread()->getLogger(); Logger *logger = Thread::getThread()->getLogger();
logger->setLogLevel(m_oldLogLevel); logger->setLogLevel(m_oldLogLevel);

View File

@ -36,6 +36,7 @@ public:
void closeEvent(QCloseEvent *e) { void closeEvent(QCloseEvent *e) {
e->ignore(); e->ignore();
} }
void expand();
protected slots: protected slots:
void onTextMessage(ELogLevel level, const QString &message); void onTextMessage(ELogLevel level, const QString &message);
void on_toggleButton_clicked(); void on_toggleButton_clicked();

View File

@ -30,6 +30,7 @@
#include "updatedlg.h" #include "updatedlg.h"
#include "server.h" #include "server.h"
#include "save.h" #include "save.h"
#include "upgrade.h"
#include <QtNetwork> #include <QtNetwork>
#include <mitsuba/core/sched_remote.h> #include <mitsuba/core/sched_remote.h>
#include <mitsuba/core/sstream.h> #include <mitsuba/core/sstream.h>
@ -398,9 +399,9 @@ void MainWindow::onNetworkFinished(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
try { try {
QSettings settings("mitsuba-renderer.org", "qtgui"); QSettings settings("mitsuba-renderer.org", "qtgui");
ProgramVersion remote(QString(reply->readAll())); Version remote(QString(reply->readAll()).toStdString());
ProgramVersion ignoredVersion(settings.value("ignoredVersion", "0.0.0").toString()); Version ignoredVersion(settings.value("ignoredVersion", "0.0.0").toString().toStdString());
ProgramVersion local(MTS_VERSION); Version local(MTS_VERSION);
if (local < remote) { if (local < remote) {
if (!m_manualUpdateCheck && remote == ignoredVersion) if (!m_manualUpdateCheck && remote == ignoredVersion)
@ -413,7 +414,7 @@ void MainWindow::onNetworkFinished(QNetworkReply *reply) {
QMessageBox::information(this, tr("Installed version is current"), QMessageBox::information(this, tr("Installed version is current"),
QString("<p>You're up to date!</p>" QString("<p>You're up to date!</p>"
"<p>Mitsuba <b>%1</b> is still the newest version available.</p>") "<p>Mitsuba <b>%1</b> is still the newest version available.</p>")
.arg(local.toString()), QMessageBox::Ok); .arg(local.toString().c_str()), QMessageBox::Ok);
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
/* Got something weird and couldn't parse the version string -- /* Got something weird and couldn't parse the version string --
@ -618,14 +619,16 @@ SceneContext *MainWindow::loadScene(const QString &qFileName) {
newResolver->addPath(filePath); newResolver->addPath(filePath);
for (int i=0; i<m_searchPaths.size(); ++i) for (int i=0; i<m_searchPaths.size(); ++i)
newResolver->addPath(m_searchPaths[i].toStdString()); newResolver->addPath(m_searchPaths[i].toStdString());
ref<SceneLoader> loadingThread
= new SceneLoader(newResolver, filename.file_string());
LoadDialog *loaddlg = new LoadDialog(this); LoadDialog *loaddlg = new LoadDialog(this);
SceneContext *result = NULL;
ref<SceneLoader> loadingThread;
loaddlg->setAttribute(Qt::WA_DeleteOnClose); loaddlg->setAttribute(Qt::WA_DeleteOnClose);
loaddlg->setWindowModality(Qt::ApplicationModal); loaddlg->setWindowModality(Qt::ApplicationModal);
loaddlg->setWindowTitle("Loading .."); loaddlg->setWindowTitle("Loading ..");
loaddlg->show(); loaddlg->show();
retry:
loadingThread = new SceneLoader(newResolver, filename.file_string());
loadingThread->start(); loadingThread->start();
while (loadingThread->isRunning()) { while (loadingThread->isRunning()) {
@ -633,14 +636,55 @@ SceneContext *MainWindow::loadScene(const QString &qFileName) {
loadingThread->wait(20); loadingThread->wait(20);
} }
loadingThread->join(); loadingThread->join();
loaddlg->close();
SceneContext *result = loadingThread->getResult(); result = loadingThread->getResult();
if (result == NULL) { if (result == NULL) {
QMessageBox::critical(this, tr("Unable to load %1").arg(qFileName), if (loadingThread->isVersionError()) {
QString(loadingThread->getError().c_str()), Version version = loadingThread->getVersion();
QMessageBox::Ok); 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; return result;
} }

View File

@ -17,7 +17,6 @@
*/ */
#include "save.h" #include "save.h"
#include <QtXml>
static QDomElement findUniqueChild(QDomElement element, const char *tagName) { static QDomElement findUniqueChild(QDomElement element, const char *tagName) {
QDomElement result; QDomElement result;
@ -101,26 +100,7 @@ static void setProperties(QDomDocument &doc, QDomElement &element,
} }
void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) { void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomDocument doc("MitsubaScene"); QDomElement root = ctx->doc.documentElement();
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 <b>%1</b>. "
"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 <b>%1</b>. "
"Was this file modified in the meantime?").arg(ctx->fileName),
QMessageBox::Ok);
file.close();
return;
}
file.close();
QDomElement root = doc.documentElement();
// ==================================================================== // ====================================================================
// Serialize the camera configuration // Serialize the camera configuration
@ -128,7 +108,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement camera = findUniqueChild(root, "camera"); QDomElement camera = findUniqueChild(root, "camera");
if (camera.isNull()) { if (camera.isNull()) {
camera = doc.createElement("camera"); camera = ctx->doc.createElement("camera");
camera.setAttribute("type", "perspective"); camera.setAttribute("type", "perspective");
root.insertAfter(camera, QDomNode()); root.insertAfter(camera, QDomNode());
} }
@ -136,7 +116,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement fovProperty = findProperty(camera, "fov"); QDomElement fovProperty = findProperty(camera, "fov");
if (fovProperty.isNull()) { if (fovProperty.isNull()) {
fovProperty = doc.createElement("float"); fovProperty = ctx->doc.createElement("float");
fovProperty.setAttribute("name", "fov"); fovProperty.setAttribute("name", "fov");
camera.insertBefore(fovProperty, QDomNode()); camera.insertBefore(fovProperty, QDomNode());
} }
@ -144,7 +124,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement focusDepthProperty = findProperty(camera, "focusDepth"); QDomElement focusDepthProperty = findProperty(camera, "focusDepth");
if (focusDepthProperty.isNull()) { if (focusDepthProperty.isNull()) {
focusDepthProperty = doc.createElement("float"); focusDepthProperty = ctx->doc.createElement("float");
focusDepthProperty.setAttribute("name", "focusDepth"); focusDepthProperty.setAttribute("name", "focusDepth");
camera.insertBefore(focusDepthProperty, QDomNode()); camera.insertBefore(focusDepthProperty, QDomNode());
} }
@ -152,13 +132,13 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement cameraTransform = findUniqueChild(camera, "transform"); QDomElement cameraTransform = findUniqueChild(camera, "transform");
if (cameraTransform.isNull()) { if (cameraTransform.isNull()) {
cameraTransform = doc.createElement("transform"); cameraTransform = ctx->doc.createElement("transform");
cameraTransform.setAttribute("name", "toWorld"); cameraTransform.setAttribute("name", "toWorld");
camera.insertBefore(cameraTransform, QDomNode()); camera.insertBefore(cameraTransform, QDomNode());
} }
removeChildren(cameraTransform); removeChildren(cameraTransform);
QDomElement lookAt = doc.createElement("lookAt"); QDomElement lookAt = ctx->doc.createElement("lookAt");
cameraTransform.insertAfter(lookAt, QDomNode()); cameraTransform.insertAfter(lookAt, QDomNode());
Vector direction = sceneCamera->getInverseViewTransform()(Vector(0,0,1)), 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)); Point t, p = sceneCamera->getInverseViewTransform()(Point(0,0,0));
if (sceneCamera->getViewTransform().det3x3() < 0) { if (sceneCamera->getViewTransform().det3x3() < 0) {
QDomElement scale = doc.createElement("scale"); QDomElement scale = ctx->doc.createElement("scale");
scale.setAttribute("x", "-1"); scale.setAttribute("x", "-1");
cameraTransform.insertBefore(scale, lookAt); cameraTransform.insertBefore(scale, lookAt);
} }
@ -182,15 +162,15 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement newSampler, sampler = findUniqueChild(camera, "sampler"); QDomElement newSampler, sampler = findUniqueChild(camera, "sampler");
if (sampler.isNull()) { if (sampler.isNull()) {
newSampler = doc.createElement("sampler"); newSampler = ctx->doc.createElement("sampler");
camera.appendChild(newSampler); camera.appendChild(newSampler);
} else { } else {
newSampler = doc.createElement("sampler"); newSampler = ctx->doc.createElement("sampler");
camera.insertAfter(newSampler, sampler); camera.insertAfter(newSampler, sampler);
camera.removeChild(sampler); camera.removeChild(sampler);
} }
setProperties(doc, newSampler, ctx->scene->getSampler()->getProperties()); setProperties(ctx->doc, newSampler, ctx->scene->getSampler()->getProperties());
// ==================================================================== // ====================================================================
// Serialize the film configuration // Serialize the film configuration
@ -198,7 +178,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement film = findUniqueChild(camera, "film"); QDomElement film = findUniqueChild(camera, "film");
if (film.isNull()) { if (film.isNull()) {
film = doc.createElement("film"); film = ctx->doc.createElement("film");
film.setAttribute("type", "exrfilm"); film.setAttribute("type", "exrfilm");
camera.insertAfter(film, QDomNode()); camera.insertAfter(film, QDomNode());
} }
@ -207,13 +187,13 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement heightProperty = findProperty(film, "height"); QDomElement heightProperty = findProperty(film, "height");
if (widthProperty.isNull()) { if (widthProperty.isNull()) {
widthProperty = doc.createElement("integer"); widthProperty = ctx->doc.createElement("integer");
widthProperty.setAttribute("name", "width"); widthProperty.setAttribute("name", "width");
film.insertBefore(widthProperty, QDomNode()); film.insertBefore(widthProperty, QDomNode());
} }
if (heightProperty.isNull()) { if (heightProperty.isNull()) {
heightProperty = doc.createElement("integer"); heightProperty = ctx->doc.createElement("integer");
heightProperty.setAttribute("name", "height"); heightProperty.setAttribute("name", "height");
film.insertBefore(heightProperty, QDomNode()); film.insertBefore(heightProperty, QDomNode());
} }
@ -227,14 +207,14 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement gamma = findProperty(film, "gamma"); QDomElement gamma = findProperty(film, "gamma");
if (method.isNull()) { if (method.isNull()) {
method = doc.createElement("string"); method = ctx->doc.createElement("string");
method.setAttribute("name", "toneMappingMethod"); method.setAttribute("name", "toneMappingMethod");
film.insertBefore(method, QDomNode()); film.insertBefore(method, QDomNode());
} }
method.setAttribute("value", ctx->toneMappingMethod == EReinhard ? "reinhard" : "gamma"); method.setAttribute("value", ctx->toneMappingMethod == EReinhard ? "reinhard" : "gamma");
if (gamma.isNull()) { if (gamma.isNull()) {
gamma = doc.createElement("float"); gamma = ctx->doc.createElement("float");
gamma.setAttribute("name", "gamma"); gamma.setAttribute("name", "gamma");
film.insertBefore(gamma, QDomNode()); film.insertBefore(gamma, QDomNode());
} }
@ -242,7 +222,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
if (ctx->toneMappingMethod == EGamma) { if (ctx->toneMappingMethod == EGamma) {
if (exposure.isNull()) { if (exposure.isNull()) {
exposure = doc.createElement("float"); exposure = ctx->doc.createElement("float");
exposure.setAttribute("name", "exposure"); exposure.setAttribute("name", "exposure");
film.insertBefore(exposure, QDomNode()); film.insertBefore(exposure, QDomNode());
} }
@ -253,12 +233,12 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
film.removeChild(reinhardBurn); film.removeChild(reinhardBurn);
} else { } else {
if (reinhardKey.isNull()) { if (reinhardKey.isNull()) {
reinhardKey = doc.createElement("float"); reinhardKey = ctx->doc.createElement("float");
reinhardKey.setAttribute("name", "reinhardKey"); reinhardKey.setAttribute("name", "reinhardKey");
film.insertBefore(reinhardKey, QDomNode()); film.insertBefore(reinhardKey, QDomNode());
} }
if (reinhardBurn.isNull()) { if (reinhardBurn.isNull()) {
reinhardBurn = doc.createElement("float"); reinhardBurn = ctx->doc.createElement("float");
reinhardBurn.setAttribute("name", "reinhardBurn"); reinhardBurn.setAttribute("name", "reinhardBurn");
film.insertBefore(reinhardBurn, QDomNode()); film.insertBefore(reinhardBurn, QDomNode());
} }
@ -279,15 +259,15 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
QDomElement newRFilter, rfilter = findUniqueChild(film, "rfilter"); QDomElement newRFilter, rfilter = findUniqueChild(film, "rfilter");
if (rfilter.isNull()) { if (rfilter.isNull()) {
newRFilter = doc.createElement("rfilter"); newRFilter = ctx->doc.createElement("rfilter");
film.appendChild(rfilter); film.appendChild(rfilter);
} else { } else {
newRFilter = doc.createElement("rfilter"); newRFilter = ctx->doc.createElement("rfilter");
film.insertAfter(newRFilter, rfilter); film.insertAfter(newRFilter, rfilter);
film.removeChild(rfilter); film.removeChild(rfilter);
} }
setProperties(doc, newRFilter, ctx->scene->getCamera()-> setProperties(ctx->doc, newRFilter, ctx->scene->getCamera()->
getFilm()->getReconstructionFilter()->getProperties()); getFilm()->getReconstructionFilter()->getProperties());
// ==================================================================== // ====================================================================
@ -295,16 +275,16 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
// ==================================================================== // ====================================================================
QDomElement oldIntegratorNode = findUniqueChild(root, "integrator"); QDomElement oldIntegratorNode = findUniqueChild(root, "integrator");
QDomElement newIntegratorNode = doc.createElement("integrator"); QDomElement newIntegratorNode = ctx->doc.createElement("integrator");
const Integrator *integrator = ctx->scene->getIntegrator(); const Integrator *integrator = ctx->scene->getIntegrator();
setProperties(doc, newIntegratorNode, integrator->getProperties()); setProperties(ctx->doc, newIntegratorNode, integrator->getProperties());
QDomElement currentIntegratorNode = newIntegratorNode; QDomElement currentIntegratorNode = newIntegratorNode;
while (integrator->getSubIntegrator() != NULL) { while (integrator->getSubIntegrator() != NULL) {
integrator = integrator->getSubIntegrator(); integrator = integrator->getSubIntegrator();
QDomElement childIntegratorNode = doc.createElement("integrator"); QDomElement childIntegratorNode = ctx->doc.createElement("integrator");
setProperties(doc, childIntegratorNode, integrator->getProperties()); setProperties(ctx->doc, childIntegratorNode, integrator->getProperties());
currentIntegratorNode.appendChild(childIntegratorNode); currentIntegratorNode.appendChild(childIntegratorNode);
currentIntegratorNode = childIntegratorNode; currentIntegratorNode = childIntegratorNode;
} }
@ -312,6 +292,7 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
root.insertBefore(newIntegratorNode, oldIntegratorNode); root.insertBefore(newIntegratorNode, oldIntegratorNode);
root.removeChild(oldIntegratorNode); root.removeChild(oldIntegratorNode);
QFile file;
file.setFileName(targetFile); file.setFileName(targetFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
QMessageBox::critical(parent, parent->tr("Unable to save"), 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 .. is more suitable for human consumption ..
Beware: the code below is tailored to Qt's Beware: the code below is tailored to Qt's
output and won't work on arbitrary XML files */ output and won't work on arbitrary XML files */
QString textContent = doc.toString(); QString textContent = ctx->doc.toString();
QTextStream input(&textContent); QTextStream input(&textContent);
QTextStream output(&file); QTextStream output(&file);
cleanupXML(input, output);
file.close();
}
void cleanupXML(QTextStream &input, QTextStream &output) {
QRegExp QRegExp
filenameRegExp("filename=\"[^\"]*\""), filenameRegExp("filename=\"[^\"]*\""),
nameRegExp("name=\"[^\"]*\""), nameRegExp("name=\"[^\"]*\""),
@ -362,10 +348,15 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
int nameMatch = nameRegExp.indexIn(line), int nameMatch = nameRegExp.indexIn(line),
filenameMatch = filenameRegExp.indexIn(line), filenameMatch = filenameRegExp.indexIn(line),
nameLength = nameRegExp.matchedLength(); nameLength = nameRegExp.matchedLength();
if (tagMatch != -1 && nameMatch != -1 && filenameMatch == -1) { if (tagMatch != -1 && nameMatch != -1 && filenameMatch == -1) {
line = line.left(tagLength) + line.mid(nameMatch, nameLength) + " " QString a = line.mid(tagLength, nameMatch-tagLength).trimmed(),
+ line.mid(tagMatch+tagLength, nameMatch-(tagMatch+tagLength)) b = line.mid(nameMatch+nameLength).trimmed();
+ line.mid(nameMatch+nameLength); 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 /* 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; hasContents = true;
} else if (line.endsWith(" >")) { } else if (line.endsWith(" >")) {
line = line.left(line.size()-2) + QString(">"); line = line.left(line.size()-2) + QString(">");
} else if (line.endsWith("?>")) {
hasContents = true;
} }
if (closeTag.indexIn(line) == 0) if (closeTag.indexIn(line) == 0)
@ -421,6 +414,4 @@ void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile) {
output << line << endl; output << line << endl;
} }
file.close();
} }

View File

@ -19,3 +19,4 @@
#include "common.h" #include "common.h"
extern void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile); extern void saveScene(QWidget *parent, SceneContext *ctx, const QString &targetFile);
extern void cleanupXML(QTextStream &input, QTextStream &output);

View File

@ -27,6 +27,7 @@
SceneLoader::SceneLoader(FileResolver *resolver, const std::string &filename) SceneLoader::SceneLoader(FileResolver *resolver, const std::string &filename)
: Thread("load"), m_resolver(resolver), m_filename(filename) { : Thread("load"), m_resolver(resolver), m_filename(filename) {
m_wait = new WaitFlag(); m_wait = new WaitFlag();
m_versionError = false;
} }
SceneLoader::~SceneLoader() { SceneLoader::~SceneLoader() {
@ -35,9 +36,7 @@ SceneLoader::~SceneLoader() {
void SceneLoader::run() { void SceneLoader::run() {
Thread::getThread()->setFileResolver(m_resolver); Thread::getThread()->setFileResolver(m_resolver);
SAXParser* parser = new SAXParser(); SAXParser* parser = new SAXParser();
std::string lowerCase = m_filename; std::string lowerCase = boost::to_lower_copy(m_filename);
for(size_t i=0; i<m_filename.size();++i)
lowerCase[i] = std::tolower(m_filename[i]);
SceneHandler *handler = new SceneHandler(parser, SceneHandler *handler = new SceneHandler(parser,
SceneHandler::ParameterMap()); SceneHandler::ParameterMap());
@ -90,7 +89,15 @@ void SceneLoader::run() {
baseName = fs::basename(filename); baseName = fs::basename(filename);
SLog(EInfo, "Parsing scene description from \"%s\" ..", m_filename.c_str()); SLog(EInfo, "Parsing scene description from \"%s\" ..", m_filename.c_str());
parser->parse(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> scene = handler->getScene(); ref<Scene> scene = handler->getScene();
scene->setSourceFile(m_filename); scene->setSourceFile(m_filename);
@ -105,10 +112,19 @@ void SceneLoader::run() {
SLog(EError, "Unable to load scene: no film found!"); SLog(EError, "Unable to load scene: no film found!");
if (scene->getLuminaires().size() == 0) if (scene->getLuminaires().size() == 0)
SLog(EError, "Unable to load scene: no light sources found!"); SLog(EError, "Unable to load scene: no light sources found!");
Vector2i size = scene->getFilm()->getSize(); Vector2i size = scene->getFilm()->getSize();
Camera *camera = scene->getCamera(); 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->scene = scene;
m_result->sceneResID = Scheduler::getInstance()->registerResource(scene); m_result->sceneResID = Scheduler::getInstance()->registerResource(scene);
m_result->renderJob = NULL; m_result->renderJob = NULL;

View File

@ -35,6 +35,8 @@ public:
inline SceneContext *getResult() { return m_result; } inline SceneContext *getResult() { return m_result; }
inline const std::string &getError() const { return m_error; } 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: protected:
virtual ~SceneLoader(); virtual ~SceneLoader();
private: private:
@ -42,6 +44,8 @@ private:
ref<WaitFlag> m_wait; ref<WaitFlag> m_wait;
SceneContext *m_result; SceneContext *m_result;
std::string m_error, m_filename; std::string m_error, m_filename;
bool m_versionError;
Version m_version;
}; };
#endif // __SCENELOADER_H #endif // __SCENELOADER_H

View File

@ -19,19 +19,19 @@
#include "ui_updatedlg.h" #include "ui_updatedlg.h"
#include "updatedlg.h" #include "updatedlg.h"
UpdateDialog::UpdateDialog(QWidget *parent, const ProgramVersion &local, UpdateDialog::UpdateDialog(QWidget *parent, const Version &local,
const ProgramVersion &remote) : QDialog(parent), const Version &remote) : QDialog(parent),
ui(new Ui::UpdateDialog) { ui(new Ui::UpdateDialog) {
ui->setupUi(this); ui->setupUi(this);
m_remoteVersion = remote.toString().c_str();
ui->versionLabel->setText(QApplication::translate("UpdateDialog", ui->versionLabel->setText(QApplication::translate("UpdateDialog",
"Version %1 has been released (you are using %2). Would you like to visit the download page?", "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 .."); ui->changeView->setHtml("Loading change log ..");
m_networkManager = new QNetworkAccessManager(this); m_networkManager = new QNetworkAccessManager(this);
connect(m_networkManager, SIGNAL(finished(QNetworkReply *)), connect(m_networkManager, SIGNAL(finished(QNetworkReply *)),
this, SLOT(onNetworkFinished(QNetworkReply *))); this, SLOT(onNetworkFinished(QNetworkReply *)));
m_networkReply = m_networkManager->get(QNetworkRequest(QUrl("http://www.mitsuba-renderer.org/changelog.html"))); m_networkReply = m_networkManager->get(QNetworkRequest(QUrl("http://www.mitsuba-renderer.org/changelog.html")));
m_remoteVersion = remote.toString();
} }
void UpdateDialog::onNetworkFinished(QNetworkReply *reply) { void UpdateDialog::onNetworkFinished(QNetworkReply *reply) {

View File

@ -29,8 +29,8 @@ namespace Ui {
class UpdateDialog : public QDialog { class UpdateDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
UpdateDialog(QWidget *parent, const ProgramVersion &local, UpdateDialog(QWidget *parent, const Version &local,
const ProgramVersion &remote); const Version &remote);
~UpdateDialog(); ~UpdateDialog();
protected slots: protected slots:
void on_skipButton_clicked(); void on_skipButton_clicked();

166
src/qtgui/upgrade.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "upgrade.h"
#include "save.h"
#include <mitsuba/core/fresolver.h>
#include <boost/algorithm/string.hpp>
#include <QtXmlPatterns>
struct VersionComparator {
inline bool operator()(const std::pair<Version, fs::path> &s1,
const std::pair<Version, fs::path> &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("<p>");
int paragraphEnd = descr.indexOf("</p>");
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<m_transformations.size(); ++i)
SLog(EInfo, " - registered transformation \"%s\", which updates to version %s",
m_transformations[i].second.filename().c_str(),
m_transformations[i].first.toString().c_str());
}
void UpgradeManager::performUpgrade(const QString &filename, const Version &version) {
QString backupFilename = filename;
if (backupFilename.endsWith(".xml", Qt::CaseInsensitive))
backupFilename.replace(backupFilename.length()-4, 4, ".bak");
SLog(EInfo, "Saving a backup copy of \"%s\" as \"%s\"",
filename.toStdString().c_str(), backupFilename.toStdString().c_str());
QFile file(filename), backupFile(backupFilename);
if (backupFile.exists())
backupFile.remove();
if (!file.copy(backupFilename))
SLog(EError, "Could not create a backup copy -- "
"stopping the upgrade operation!");
if (!file.open(QIODevice::ReadOnly))
SLog(EError, "Unable to open \"%s\" with read access -- stopping "
"the upgrade operation.", filename.toStdString().c_str());
QByteArray inputArray = file.readAll(), outputArray;
file.close();
QBuffer inputBuffer(&inputArray);
QBuffer outputBuffer(&outputArray);
XSLTMessageHandler handler;
int nTransformations=0;
for (size_t i=0; i<m_transformations.size(); ++i) {
if (m_transformations[i].first <= version)
continue;
inputBuffer.open(QIODevice::ReadOnly);
outputBuffer.open(QIODevice::WriteOnly | QIODevice::Truncate);
SLog(EInfo, "Applying transformation \"%s\" ..",
m_transformations[i].second.filename().c_str());
std::string trafoFilename = m_transformations[i].second.file_string();
QFile trafoFile(trafoFilename.c_str());
if (!trafoFile.open(QIODevice::ReadOnly | QIODevice::Text))
SLog(EError, "Unable to open the stylesheet \"%s\" -- stopping "
"the upgrade operation.", trafoFilename.c_str());
QXmlQuery query(QXmlQuery::XSLT20);
query.setMessageHandler(&handler);
query.setFocus(&inputBuffer);
query.setQuery(&trafoFile);
if (!query.isValid())
SLog(EError, "Unable to parse the stylesheet \"%s\" -- stopping "
"the upgrade operation.", trafoFilename.c_str());
SLog(EInfo, "Transformation is ready, running it now..");
QXmlFormatter formatter(query, &outputBuffer);
formatter.setIndentationDepth(1);
query.evaluateTo(&formatter);
if (handler.fatalError())
SLog(EError, "A fatal error was encountered -- stopping "
"the upgrade operation.");
/* Swap the outputs */
inputBuffer.close();
outputBuffer.close();
inputArray = outputArray;
++nTransformations;
}
if (nTransformations == 0)
SLog(EError, "Strange -- no transformations were applied? "
"Stopping the upgrade operation.");
/* Done, write back to disk */
SLog(EInfo, "Successfully applied %i transformations, writing the result "
"to \"%s\" ..", nTransformations, filename.toStdString().c_str());
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
SLog(EError, "Unable to open \"%s\" with write access -- stopping "
"the upgrade operation.", filename.toStdString().c_str());
}
QTextStream input(inputArray);
QTextStream output(&file);
output << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
<< endl << endl;
cleanupXML(input, output);
file.close();
}

35
src/qtgui/upgrade.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<std::pair<Version, fs::path> > m_transformations;
};
#endif // __UPGRADEMGR_H

View File

@ -18,8 +18,7 @@
#include <mitsuba/render/texture.h> #include <mitsuba/render/texture.h>
#include <mitsuba/render/shape.h> #include <mitsuba/render/shape.h>
#include <mitsuba/core/properties.h> #include <mitsuba/hw/basicshader.h>
#include <mitsuba/hw/gpuprogram.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -31,6 +30,10 @@ MTS_NAMESPACE_BEGIN
class ScalingTexture : public Texture { class ScalingTexture : public Texture {
public: public:
ScalingTexture(const Properties &props) : Texture(props) { 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) if (props.hasProperty("scale") && props.getType("scale") == Properties::EFloat)
m_scale = Spectrum(props.getFloat("scale", 1.0f)); m_scale = Spectrum(props.getFloat("scale", 1.0f));
else else
@ -49,11 +52,10 @@ public:
} }
void addChild(const std::string &name, ConfigurableObject *child) { 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<Texture *>(child); m_nested = static_cast<Texture *>(child);
} else { else
Texture::addChild(name, child); Texture::addChild(name, child);
}
} }
Spectrum getValue(const Intersection &its) const { Spectrum getValue(const Intersection &its) const {