metadata
Wenzel Jakob 2012-11-02 19:41:04 -04:00
commit f66737cf39
57 changed files with 704 additions and 519 deletions

View File

@ -6,3 +6,4 @@ e3c0182ba64b77319ce84c9e2a8581649e68273d v0.2.1
cb6e89af8012fac22cc0f3c5ad247c98c701bdda v0.3.0 cb6e89af8012fac22cc0f3c5ad247c98c701bdda v0.3.0
ee26517b27207353b0c8a7d357bcb4977b5d93fb v0.4.0 ee26517b27207353b0c8a7d357bcb4977b5d93fb v0.4.0
7db07694ea00eb1655f7a1adcc3ae880e8e116f9 v0.4.1 7db07694ea00eb1655f7a1adcc3ae880e8e116f9 v0.4.1
13a39b11aceee517c19d2e2cec2e6b875546062c v0.4.2

View File

@ -2,4 +2,4 @@
cp /opt/intel/composer_xe_*/compiler/lib/libiomp5.dylib Mitsuba.app/Contents/Frameworks cp /opt/intel/composer_xe_*/compiler/lib/libiomp5.dylib Mitsuba.app/Contents/Frameworks
find Mitsuba.app/Contents/MacOS/ Mitsuba.app/plugins -type f | xargs -n 1 install_name_tool -change libiomp5.dylib @rpath/libiomp5.dylib find Mitsuba.app/Contents/MacOS/ Mitsuba.app/plugins -type f | xargs -n 1 install_name_tool -change libiomp5.dylib @rpath/libiomp5.dylib
find Mitsuba.app/Contents/Frameworks/libmitsuba-* -type f | xargs -n 1 install_name_tool -change libiomp5.dylib @rpath/libiomp5.dylib find Mitsuba.app/Contents/Frameworks/libmitsuba-* -type f | xargs -n 1 install_name_tool -change libiomp5.dylib @rpath/libiomp5.dylib
find Mitsuba.app/Contents/python -type f | xargs -n 1 install_name_tool -change libiomp5.dylib @rpath/libiomp5.dylib find Mitsuba.app/python -type f | xargs -n 1 install_name_tool -change libiomp5.dylib @rpath/libiomp5.dylib

View File

@ -1,3 +1,18 @@
mitsuba (0.4.2-1) unstable; urgency=low
* Volumetric path tracers: improved sampling when dealing with index-matched medium transitions. This is essentially a re-implementation of an optimization that Mitsuba 0.3.1 already had, but which got lost in the bidirectional rewrite.
* Batch tonemapper: due to an unfortunate bug, the batch tonemapper in the last release produced invalid results for images containing an alpha channel. This is now fixed.
* Shapes: corrected some differential geometry issues in the "cylinder" and "rectangle" shapes.
* MLT: fixed 2-stage MLT, which was producing incorrect results.
* MEPT: fixed the handling of directional light sources.
* Robustness: got rid of various corner-cases that could produce NaNs.
* Filenames: to facilitate loading scenes created on Windows/OSX, the Linux version now resolves files case-insensitively if they could not be found after a case-sensitive search.
* Python: added Python bindings for shapes and triangle meshes. The Python plugin should now be easier to load (previously, this was unfortunately rather difficult on several platforms). The documentation was also given an overhaul.
* Particle tracing: I've decided to disable the adjoint BSDF for shading normals in the particle tracer, since it causes an unacceptable amount of variance in scenes containing poorly tesselated geometry. This affects the plugins ptracer, ppm, sppm and photonmapper.
* Subsurface scattering: fixed parallel network renderings involving the dipole model.
* Homogeneous medium & dipole: added many more material presets by Narasimhan et al.
* OBJ loader: further robustness improvements to the OBJ loader and the associated MTL material translator.
-- Wenzel Jakob <wenzel@cs.cornell.edu> Wed, 31 Oct 2012 00:00:00 -0400
mitsuba (0.4.1-1) unstable; urgency=low mitsuba (0.4.1-1) unstable; urgency=low
* negative pixel values in textures and environment maps are handled more gracefully. * negative pixel values in textures and environment maps are handled more gracefully.
* minor robustness improvements to the OBJ and COLLADA importers. * minor robustness improvements to the OBJ and COLLADA importers.

View File

@ -1,5 +1,5 @@
Name: mitsuba Name: mitsuba
Version: 0.4.1 Version: 0.4.2
Release: 1%{?dist} Release: 1%{?dist}
Summary: Mitsuba renderer Summary: Mitsuba renderer
Group: Applications/Graphics Group: Applications/Graphics
@ -62,6 +62,9 @@ rm -rf $RPM_BUILD_ROOT
/usr/include/* /usr/include/*
%changelog %changelog
* Wed Oct 31 2012 Wenzel Jakob <wenzel@cs.cornell.edu> 0.4.2%{?dist}
- Upgrade to version 0.4.2
* Wed Oct 10 2012 Wenzel Jakob <wenzel@cs.cornell.edu> 0.4.1%{?dist} * Wed Oct 10 2012 Wenzel Jakob <wenzel@cs.cornell.edu> 0.4.1%{?dist}
- Upgrade to version 0.4.1 - Upgrade to version 0.4.1

View File

@ -129,9 +129,8 @@ scons: $\texttt{done}$ building targets.
\end{shell} \end{shell}
To run the renderer from the command line, you first have to import it into your shell environment: To run the renderer from the command line, you first have to import it into your shell environment:
\begin{shell} \begin{shell}
$\text{\$}$ . setpath.sh $\text{\$}$ source setpath.sh
\end{shell} \end{shell}
(note the period at the beginning -- this assumes that you are using \code{bash}).
Having set up everything, you can now move on to \secref{basics}. Having set up everything, you can now move on to \secref{basics}.
\subsubsection{Creating Debian or Ubuntu Linux packages} \subsubsection{Creating Debian or Ubuntu Linux packages}
The preferred way of redistristributing executables on Debian or Ubuntu Linux is to create The preferred way of redistristributing executables on Debian or Ubuntu Linux is to create
@ -208,9 +207,8 @@ scons: $\texttt{done}$ building targets.
\end{shell} \end{shell}
To run the renderer from the command line, you first have to import it into your shell environment: To run the renderer from the command line, you first have to import it into your shell environment:
\begin{shell} \begin{shell}
$\text{\$}$ . setpath.sh $\text{\$}$ source setpath.sh
\end{shell} \end{shell}
(note the period at the beginning -- this assumes that you are using \code{bash}).
Having set up everything, you can now move on to \secref{basics}. Having set up everything, you can now move on to \secref{basics}.
\subsubsection{Creating Fedora Core packages} \subsubsection{Creating Fedora Core packages}
To create \code{RPM} packages, you will need to install the \code{RPM} development tools: To create \code{RPM} packages, you will need to install the \code{RPM} development tools:
@ -261,9 +259,9 @@ scons: $\texttt{done}$ building targets.
\end{shell} \end{shell}
To run the renderer from the command line, you first have to import it into your shell environment: To run the renderer from the command line, you first have to import it into your shell environment:
\begin{shell} \begin{shell}
$\text{\$}$ . setpath.sh $\text{\$}$ source setpath.sh
\end{shell} \end{shell}
(note the period at the beginning -- this assumes that you are using \code{bash}).
Having set up everything, you can now move on to \secref{basics}. Having set up everything, you can now move on to \secref{basics}.
\subsubsection{Creating Arch Linux packages} \subsubsection{Creating Arch Linux packages}
Mitsuba ships with a \code{PKGBUILD} file, which automatically builds Mitsuba ships with a \code{PKGBUILD} file, which automatically builds
@ -350,7 +348,6 @@ scons: $\texttt{done}$ building targets.
\end{shell} \end{shell}
To run the renderer from the command line, you first have to import it into your shell environment: To run the renderer from the command line, you first have to import it into your shell environment:
\begin{shell} \begin{shell}
$\text{\$}$ . setpath.sh $\text{\$}$ source setpath.sh
\end{shell} \end{shell}
(note the period at the beginning -- this assumes that you are using \code{bash}).

View File

@ -13,7 +13,7 @@ using hardware acceleration, which certainly doesn't fit into the sampling-based
For that reason, it must be implemented as a generic integrator. For that reason, it must be implemented as a generic integrator.
Generally, if you can package up your code to fit into the Generally, if you can package up your code to fit into the
\code{SampleIntegrator} interface, you should do it, because you'll get \code{SamplingIntegrator} interface, you should do it, because you'll get
parallelization and network rendering essentially for free. This is done parallelization and network rendering essentially for free. This is done
by transparently sending instances of your integrator class to all participating cores by transparently sending instances of your integrator class to all participating cores
and assigning small image blocks for each one to work on. Also, sampling-based and assigning small image blocks for each one to work on. Also, sampling-based
@ -38,12 +38,12 @@ In Mitsuba's \code{src/integrators} directory, create a file named
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
class MyIntegrator : public SampleIntegrator { class MyIntegrator : public SamplingIntegrator {
public: public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
}; };
MTS_IMPLEMENT_CLASS_S(MyIntegrator, false, SampleIntegrator) MTS_IMPLEMENT_CLASS_S(MyIntegrator, false, SamplingIntegrator)
MTS_EXPORT_PLUGIN(MyIntegrator, "A contrived integrator"); MTS_EXPORT_PLUGIN(MyIntegrator, "A contrived integrator");
MTS_NAMESPACE_END MTS_NAMESPACE_END
\end{cpp} \end{cpp}
@ -67,7 +67,7 @@ need to be provided by the implementation --- we'll add those in a moment.
The three following parameters specify the name of this class (\code{MyIntegrator}), The three following parameters specify the name of this class (\code{MyIntegrator}),
the fact that it is \emph{not} an abstract class (\code{false}), and the name of its the fact that it is \emph{not} an abstract class (\code{false}), and the name of its
parent class (\code{SampleIntegrator}). parent class (\code{SamplingIntegrator}).
Just below, you can see a line that starts with Just below, you can see a line that starts with
\code{MTS\_EXPORT\_PLUGIN}. As the name suggests, this line is only necessary \code{MTS\_EXPORT\_PLUGIN}. As the name suggests, this line is only necessary
@ -80,7 +80,7 @@ Let's add an instance variable and a constructor:
\begin{cpp} \begin{cpp}
public: public:
/// Initialize the integrator with the specified properties /// Initialize the integrator with the specified properties
MyIntegrator(const Properties &props) : SampleIntegrator(props) { MyIntegrator(const Properties &props) : SamplingIntegrator(props) {
Spectrum defaultColor; Spectrum defaultColor;
defaultColor.fromLinearRGB(0.2f, 0.5f, 0.2f); defaultColor.fromLinearRGB(0.2f, 0.5f, 0.2f);
m_color = props.getSpectrum("color", defaultColor); m_color = props.getSpectrum("color", defaultColor);
@ -106,13 +106,13 @@ Next, we need to add serialization and unserialization support:
\begin{cpp} \begin{cpp}
/// Unserialize from a binary data stream /// Unserialize from a binary data stream
MyIntegrator(Stream *stream, InstanceManager *manager) MyIntegrator(Stream *stream, InstanceManager *manager)
: SampleIntegrator(stream, manager) { : SamplingIntegrator(stream, manager) {
m_color = Spectrum(stream); m_color = Spectrum(stream);
} }
/// Serialize to a binary data stream /// Serialize to a binary data stream
void serialize(Stream *stream, InstanceManager *manager) const { void serialize(Stream *stream, InstanceManager *manager) const {
SampleIntegrator::serialize(stream, manager); SamplingIntegrator::serialize(stream, manager);
m_color.serialize(stream); m_color.serialize(stream);
} }
\end{cpp} \end{cpp}
@ -205,11 +205,12 @@ we can override the \code{preprocess} function:
bool preprocess(const Scene *scene, RenderQueue *queue, bool preprocess(const Scene *scene, RenderQueue *queue,
const RenderJob *job, int sceneResID, int cameraResID, const RenderJob *job, int sceneResID, int cameraResID,
int samplerResID) { int samplerResID) {
SampleIntegrator::preprocess(scene, queue, job, sceneResID, SamplingIntegrator::preprocess(scene, queue, job, sceneResID,
cameraResID, samplerResID); cameraResID, samplerResID);
const AABB &sceneAABB = scene->getAABB(); const AABB &sceneAABB = scene->getAABB();
Point cameraPosition = scene->getCamera()->getPosition(); /* Find the camera position at t=0 seconds */
Point cameraPosition = scene->getSensor()->getWorldTransform()->eval(0).transformAffine(Point(0.0f));
m_maxDist = - std::numeric_limits<Float>::infinity(); m_maxDist = - std::numeric_limits<Float>::infinity();
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
@ -276,7 +277,7 @@ into a scene XML file:
\end{xml} \end{xml}
To support this kind of complex interaction, some information needs to be passed between the To support this kind of complex interaction, some information needs to be passed between the
integrators, and the \code{RadianceQueryRecord} parameter of the function integrators, and the \code{RadianceQueryRecord} parameter of the function
\code{SampleIntegrator::Li} is used for this. \code{SamplingIntegrator::Li} is used for this.
This brings us back to the odd way of computing an intersection a moment ago: This brings us back to the odd way of computing an intersection a moment ago:
the reason why we didn't just do this by calling the reason why we didn't just do this by calling

View File

@ -39,7 +39,7 @@
\setcounter{secnumdepth}{3} \setcounter{secnumdepth}{3}
\setcounter{tocdepth}{3} \setcounter{tocdepth}{3}
\newcommand{\MitsubaVersion}{0.4.1} \newcommand{\MitsubaVersion}{0.4.2}
\newcommand{\MitsubaYear}{2012} \newcommand{\MitsubaYear}{2012}
\typearea[current]{last} \typearea[current]{last}

View File

@ -1,14 +1,21 @@
\section{Python integration} \section{Python integration}
\label{sec:python} \label{sec:python}
A recent feature of Mitsuba is a simple Python interface to the renderer API. A recent feature of Mitsuba is a Python interface to the renderer API.
While the interface is still limited at this point, it can already be While the interface is still limited at this point, it can already be
used for many useful purposes. To access the API, start your Python used for many useful purposes. To access the API, start your Python
interpreter and enter interpreter and enter
\begin{python} \begin{python}
import mitsuba import mitsuba
\end{python} \end{python}
\paragraph{Mac OS:}
For this to work on MacOS X, you will first have to run the ``\emph{Apple For this to work on MacOS X, you will first have to run the ``\emph{Apple
Menu}$\to$\emph{Command-line access}'' menu item from within Mitsuba. Menu}$\to$\emph{Command-line access}'' menu item from within Mitsuba.
In the unlikely case that you run into shared library loading issues (this is
taken care of by default), you may have to set the \code{LD\_LIBRARY\_PATH}
environment variable before starting Python so that it points to where the
Mitsuba libraries are installed (e.g. the \code{Mitsuba.app/Contents/Frameworks}
directory).
\paragraph{Windows and Linux:}
On Windows and \emph{non-packaged} Linux builds, you may have to explicitly On Windows and \emph{non-packaged} Linux builds, you may have to explicitly
specify the required extension search path before issuing the \code{import} command, e.g.: specify the required extension search path before issuing the \code{import} command, e.g.:
\begin{python} \begin{python}
@ -29,6 +36,9 @@ os.environ['PATH'] = 'path-to-mitsuba-directory' + os.pathsep + os.environ['PATH
import mitsuba import mitsuba
\end{python} \end{python}
In rare cases when running on Linux, it may also be necessary to set the
\code{LD\_LIBRARY\_PATH} environment variable before starting Python so that it
points to where the Mitsuba core libraries are installed.
For an overview of the currently exposed API subset, please refer For an overview of the currently exposed API subset, please refer
to the following page: \url{http://www.mitsuba-renderer.org/api/group__libpython.html}. to the following page: \url{http://www.mitsuba-renderer.org/api/group__libpython.html}.
@ -64,8 +74,8 @@ classes, function, or entire namespaces when running an interactive Python shell
... ...
\end{shell} \end{shell}
The docstrings list the currently exported functionality, as well as C++ and Python signatures, but they The docstrings list the currently exported functionality, as well as C++ and Python signatures, but they
don't document what these functions actually do. The web API documentation is the preferred source for don't document what these functions actually do. The web API documentation is
this information. the preferred source of this information.
\subsection{Basics} \subsection{Basics}
Generally, the Python API tries to mimic the C++ API as closely as possible. Generally, the Python API tries to mimic the C++ API as closely as possible.

View File

@ -70,6 +70,10 @@ struct Normal : public TVector3<Float> {
} }
}; };
inline Normal normalize(const Normal &n) {
return n / n.length();
}
MTS_NAMESPACE_END MTS_NAMESPACE_END
#endif /* __MITSUBA_CORE_NORMAL_H_ */ #endif /* __MITSUBA_CORE_NORMAL_H_ */

View File

@ -40,7 +40,7 @@ extern const int MTS_EXPORT_CORE primeTable[primeTableSize];
/// Van der Corput radical inverse in base 2 with single precision /// Van der Corput radical inverse in base 2 with single precision
inline float radicalInverse2Single(uint32_t n, uint32_t scramble = 0U) { inline float radicalInverse2Single(uint32_t n, uint32_t scramble = 0U) {
/* Efficiently reverse the bits in 'n' using binary operations */ /* Efficiently reverse the bits in 'n' using binary operations */
#if defined __GNUC__ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))) || defined(__clang__)
n = __builtin_bswap32(n); n = __builtin_bswap32(n);
#else #else
n = (n << 16) | (n >> 16); n = (n << 16) | (n >> 16);
@ -59,7 +59,7 @@ inline float radicalInverse2Single(uint32_t n, uint32_t scramble = 0U) {
/// Van der Corput radical inverse in base 2 with double precision /// Van der Corput radical inverse in base 2 with double precision
inline double radicalInverse2Double(uint64_t n, uint64_t scramble = 0ULL) { inline double radicalInverse2Double(uint64_t n, uint64_t scramble = 0ULL) {
/* Efficiently reverse the bits in 'n' using binary operations */ /* Efficiently reverse the bits in 'n' using binary operations */
#if defined __GNUC__ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))) || defined(__clang__)
n = __builtin_bswap64(n); n = __builtin_bswap64(n);
#else #else
n = (n << 32) | (n >> 32); n = (n << 32) | (n >> 32);

View File

@ -417,8 +417,10 @@ public:
* *
* Note that the resource's won't be removed until all processes using * Note that the resource's won't be removed until all processes using
* it have terminated) * it have terminated)
*
* \return \c false if the resource could not be found
*/ */
void unregisterResource(int id); bool unregisterResource(int id);
/** /**
* \brief Return the ID of a registered resource * \brief Return the ID of a registered resource

View File

@ -185,6 +185,9 @@ extern MTS_EXPORT_CORE int mts_omp_get_thread_num();
#define mts_omp_get_max_threads omp_get_max_threads #define mts_omp_get_max_threads omp_get_max_threads
#define mts_omp_get_thread_num omp_get_thread_num #define mts_omp_get_thread_num omp_get_thread_num
#endif #endif
#else
#define mts_omp_get_max_threads() 1
#define mts_omp_get_thread_num() 0
#endif #endif
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -358,11 +358,14 @@ extern MTS_EXPORT_CORE bool solveQuadraticDouble(double a, double b,
* Position of the last knot * Position of the last knot
* \param size * \param size
* Denotes the size of the \c data array * Denotes the size of the \c data array
* \param extrapolate
* Extrapolate data values when \c x is out of range? (default: \c false)
* \return * \return
* The interpolated value or zero when \c x lies outside of [\c min, \c max] * The interpolated value or zero when <tt>extrapolate=false</tt>tt>
* and \c x lies outside of [\c min, \c max]
*/ */
extern MTS_EXPORT_CORE Float interpCubic1D(Float x, const Float *data, extern MTS_EXPORT_CORE Float interpCubic1D(Float x, const Float *data,
Float min, Float max, size_t size); Float min, Float max, size_t size, bool extrapolate = false);
/** /**
* \brief Evaluate a cubic spline interpolant of an \a irregularly sampled 1D function * \brief Evaluate a cubic spline interpolant of an \a irregularly sampled 1D function
@ -382,11 +385,14 @@ extern MTS_EXPORT_CORE Float interpCubic1D(Float x, const Float *data,
* the entries of \c nodes. * the entries of \c nodes.
* \param size * \param size
* Denotes the size of the \c data array * Denotes the size of the \c data array
* \param extrapolate
* Extrapolate data values when \c x is out of range? (default: \c false)
* \return * \return
* The interpolated value or zero when \c x lies outside of \a [\c min, \c max] * The interpolated value or zero when <tt>extrapolate=false</tt>tt>
* and \c x lies outside of \a [\c min, \c max]
*/ */
extern MTS_EXPORT Float interpCubic1DIrregular(Float x, const Float *nodes, extern MTS_EXPORT Float interpCubic1DIrregular(Float x, const Float *nodes,
const Float *data, size_t size); const Float *data, size_t size, bool extrapolate = false);
/** /**
* \brief Evaluate a cubic spline interpolant of a regularly sampled 2D function * \brief Evaluate a cubic spline interpolant of a regularly sampled 2D function
@ -407,11 +413,14 @@ extern MTS_EXPORT Float interpCubic1DIrregular(Float x, const Float *nodes,
* Position of the last knot on each dimension * Position of the last knot on each dimension
* \param size * \param size
* Denotes the size of the \c data array (along each dimension) * Denotes the size of the \c data array (along each dimension)
* \param extrapolate
* Extrapolate data values when \c p is out of range? (default: \c false)
* \return * \return
* The interpolated value or zero when \c p lies outside of the knot range * The interpolated value or zero when <tt>extrapolate=false</tt>tt> and
* \c p lies outside of the knot range
*/ */
extern MTS_EXPORT_CORE Float interpCubic2D(const Point2 &p, const Float *data, extern MTS_EXPORT_CORE Float interpCubic2D(const Point2 &p, const Float *data,
const Point2 &min, const Point2 &max, const Size2 &size); const Point2 &min, const Point2 &max, const Size2 &size, bool extrapolate = false);
/** /**
* \brief Evaluate a cubic spline interpolant of an \a irregularly sampled 2D function * \brief Evaluate a cubic spline interpolant of an \a irregularly sampled 2D function
@ -435,11 +444,14 @@ extern MTS_EXPORT_CORE Float interpCubic2D(const Point2 &p, const Float *data,
* Consecutive entries of this array correspond to increments in the 'x' coordinate. * Consecutive entries of this array correspond to increments in the 'x' coordinate.
* \param size * \param size
* Denotes the size of the \c data array (along each dimension) * Denotes the size of the \c data array (along each dimension)
* \param extrapolate
* Extrapolate data values when \c p is out of range? (default: \c false)
* \return * \return
* The interpolated value or zero when \c p lies outside of the knot range * The interpolated value or zero when <tt>extrapolate=false</tt>tt> and
* \c p lies outside of the knot range
*/ */
extern MTS_EXPORT_CORE Float interpCubic2DIrregular(const Point2 &p, const Float **nodes, extern MTS_EXPORT_CORE Float interpCubic2DIrregular(const Point2 &p, const Float **nodes,
const Float *data, const Size2 &size); const Float *data, const Size2 &size, bool extrapolate = false);
/** /**
* \brief Evaluate a cubic spline interpolant of a regularly sampled 3D function * \brief Evaluate a cubic spline interpolant of a regularly sampled 3D function
@ -461,11 +473,14 @@ extern MTS_EXPORT_CORE Float interpCubic2DIrregular(const Point2 &p, const Float
* Position of the last knot on each dimension * Position of the last knot on each dimension
* \param size * \param size
* Denotes the size of the \c data array (along each dimension) * Denotes the size of the \c data array (along each dimension)
* \param extrapolate
* Extrapolate data values when \c p is out of range? (default: \c false)
* \return * \return
* The interpolated value or zero when \c p lies outside of the knot range * The interpolated value or zero when <tt>extrapolate=false</tt>tt> and
* \c p lies outside of the knot range
*/ */
extern MTS_EXPORT_CORE Float interpCubic3D(const Point3 &p, const Float *data, extern MTS_EXPORT_CORE Float interpCubic3D(const Point3 &p, const Float *data,
const Point3 &min, const Point3 &max, const Size3 &size); const Point3 &min, const Point3 &max, const Size3 &size, bool extrapolate = false);
/** /**
* \brief Evaluate a cubic spline interpolant of an \a irregularly sampled 3D function * \brief Evaluate a cubic spline interpolant of an \a irregularly sampled 3D function
@ -490,11 +505,14 @@ extern MTS_EXPORT_CORE Float interpCubic3D(const Point3 &p, const Float *data,
* then 'y', and finally 'z' increments. * then 'y', and finally 'z' increments.
* \param size * \param size
* Denotes the size of the \c data array (along each dimension) * Denotes the size of the \c data array (along each dimension)
* \param extrapolate
* Extrapolate data values when \c p is out of range? (default: \c false)
* \return * \return
* The interpolated value or zero when \c p lies outside of the knot range * The interpolated value or zero when <tt>extrapolate=false</tt>tt> and
* \c p lies outside of the knot range
*/ */
extern MTS_EXPORT_CORE Float interpCubic3DIrregular(const Point3 &p, const Float **nodes, extern MTS_EXPORT_CORE Float interpCubic3DIrregular(const Point3 &p, const Float **nodes,
const Float *data, const Size3 &size); const Float *data, const Size3 &size, bool extrapolate = false);
//// Convert radians to degrees //// Convert radians to degrees
inline Float radToDeg(Float value) { return value * (180.0f / M_PI); } inline Float radToDeg(Float value) { return value * (180.0f / M_PI); }

View File

@ -26,7 +26,7 @@ MTS_NAMESPACE_BEGIN
* \brief Current release of Mitsuba * \brief Current release of Mitsuba
* \ingroup libcore * \ingroup libcore
*/ */
#define MTS_VERSION "0.4.1" #define MTS_VERSION "0.4.2"
/** /**
* \brief Year of the current release * \brief Year of the current release

View File

@ -206,8 +206,15 @@ public:
*/ */
void computeUVTangents(); void computeUVTangents();
/// Generate surface normals /**
void computeNormals(); * \brief Generate smooth vertex normals?
*
* \param force
* When this parameter is set to true, the function
* generates normals <em>even</em> when there are
* already existing ones.
*/
void computeNormals(bool force = false);
/** /**
* \brief Rebuild the mesh so that adjacent faces * \brief Rebuild the mesh so that adjacent faces

27
setpath.csh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/tcsh
set called=($_)
if ("$called" != "") then
set reldir=`dirname $called[2]`
else if ("$0" != "tcsh") then
set reldir=`dirname 0`
else
echo "Unable to detect path!"
exit 1
endif
set MITSUBA_DIR=`cd $reldir && pwd`
if ("`uname`" == "Darwin") then
setenv PATH "$MITSUBA_DIR/Mitsuba.app/Contents/MacOS:$PATH"
else
if (! ($?LD_LIBRARY_PATH) ) then
setenv LD_LIBRARY_PATH "$MITSUBA_DIR/dist"
else
setenv LD_LIBRARY_PATH "$MITSUBA_DIR/dist:$LD_LIBRARY_PATH"
endif
setenv PATH "$MITSUBA_DIR/dist:$PATH"
# Generate core dumps if something goes wrong
limit coredumpsize 1000000000
endif
unset reldir

0
setpath.sh Normal file → Executable file
View File

View File

@ -65,6 +65,10 @@ MTS_NAMESPACE_BEGIN
* This parameter can be used to scale the the amount of illumination * This parameter can be used to scale the the amount of illumination
* emitted by the sun emitter. \default{1} * emitted by the sun emitter. \default{1}
* } * }
* \parameter{sunRadiusScale}{\Float}{
* Scale factor to adjust the radius of the sun, while preserving its power.
* Set to \code{0} to turn it into a directional light source.
* }
* \parameter{samplingWeight}{\Float}{ * \parameter{samplingWeight}{\Float}{
* Specifies the relative amount of samples * Specifies the relative amount of samples
* allocated to this emitter. \default{1} * allocated to this emitter. \default{1}
@ -74,7 +78,6 @@ MTS_NAMESPACE_BEGIN
* Preetham et al. \cite{Preetham1999Practical}. Using the provided position * Preetham et al. \cite{Preetham1999Practical}. Using the provided position
* and time information (see \pluginref{sky} for details), it can determine the * and time information (see \pluginref{sky} for details), it can determine the
* position of the sun as seen from the position of the observer. * position of the sun as seen from the position of the observer.
*
* The radiance arriving at the earth surface is then found based on the spectral * The radiance arriving at the earth surface is then found based on the spectral
* emission profile of the sun and the extinction cross-section of the * emission profile of the sun and the extinction cross-section of the
* atmosphere (which depends on the \code{turbidity} and the zenith angle of the sun). * atmosphere (which depends on the \code{turbidity} and the zenith angle of the sun).
@ -146,6 +149,23 @@ public:
Emitter *getElement(size_t i) { Emitter *getElement(size_t i) {
if (i != 0) if (i != 0)
return NULL; return NULL;
if (m_sunRadiusScale == 0) {
Properties props("directional");
const Transform &trafo = m_worldTransform->eval(0);
props.setVector("direction", -trafo(m_sunDir));
props.setFloat("samplingWeight", m_samplingWeight);
props.setSpectrum("irradiance", m_radiance * m_solidAngle);
Emitter *emitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject(
MTS_CLASS(Emitter), props));
emitter->configure();
return emitter;
}
/* Rasterizing the sphere to an environment map and checking the /* Rasterizing the sphere to an environment map and checking the
individual pixels for coverage (which is what Mitsuba 0.3.0 did) individual pixels for coverage (which is what Mitsuba 0.3.0 did)
was slow and not very effective; for instance the power varied was slow and not very effective; for instance the power varied

View File

@ -74,16 +74,22 @@ MTS_NAMESPACE_BEGIN
* This parameter can be used to separately scale the the amount of illumination * This parameter can be used to separately scale the the amount of illumination
* emitted by the sky.\default{1} * emitted by the sky.\default{1}
* } * }
* \parameter{sunRadiusScale}{\Float}{
* Scale factor to adjust the radius of the sun, while preserving its power.
* Set to \code{0} to turn it into a directional light source.
* }
* } * }
* \vspace{-3mm}
* *
* \renderings{ * \renderings{
* \medrendering{\pluginref{sky} emitter}{emitter_sunsky_sky} * \medrendering{\pluginref{sky} emitter}{emitter_sunsky_sky}
* \medrendering{\pluginref{sun} emitter}{emitter_sunsky_sun} * \medrendering{\pluginref{sun} emitter}{emitter_sunsky_sun}
* \medrendering{\pluginref{sunsky} emitter}{emitter_sunsky_sunsky} * \medrendering{\pluginref{sunsky} emitter}{emitter_sunsky_sunsky}
* \vspace{-2mm}
* \caption{A coated rough copper test ball lit with the three * \caption{A coated rough copper test ball lit with the three
* provided daylight illumination models} * provided daylight illumination models}
* } * }
* \vspace{5mm} * \vspace{1mm}
* This convenience plugin has the sole purpose of instantiating * This convenience plugin has the sole purpose of instantiating
* \pluginref{sun} and \pluginref{sky} and merging them into a joint * \pluginref{sun} and \pluginref{sky} and merging them into a joint
* environment map. Please refer to these plugins individually for more * environment map. Please refer to these plugins individually for more
@ -95,7 +101,8 @@ public:
: Emitter(props) { : Emitter(props) {
Float scale = props.getFloat("scale", 1.0f), Float scale = props.getFloat("scale", 1.0f),
sunScale = props.getFloat("sunScale", scale), sunScale = props.getFloat("sunScale", scale),
skyScale = props.getFloat("skyScale", scale); skyScale = props.getFloat("skyScale", scale),
sunRadiusScale = props.getFloat("sunRadiusScale", 1.0f);
const Transform &trafo = m_worldTransform->eval(0); const Transform &trafo = m_worldTransform->eval(0);
@ -159,39 +166,53 @@ public:
props.getFloat("turbidity", 3.0f)) * sunScale; props.getFloat("turbidity", 3.0f)) * sunScale;
sun.elevation *= props.getFloat("stretch", 1.0f); sun.elevation *= props.getFloat("stretch", 1.0f);
Frame sunFrame = Frame(toSphere(sun)); Frame sunFrame = Frame(toSphere(sun));
Float theta = degToRad(SUN_APP_RADIUS * 0.5f); Float theta = degToRad(SUN_APP_RADIUS * 0.5f);
size_t pixelCount = resolution*resolution/2; if (sunRadiusScale == 0) {
Float cosTheta = std::cos(theta * props.getFloat("sunRadiusScale", 1.0f)); Float solidAngle = 2 * M_PI * (1 - std::cos(theta));
Properties props("directional");
props.setVector("direction", -trafo(sunFrame.n));
props.setFloat("samplingWeight", m_samplingWeight);
props.setSpectrum("irradiance", sunRadiance * solidAngle);
/* Ratio of the sphere that is covered by the sun */ m_dirEmitter = static_cast<Emitter *>(
Float coveredPortion = 0.5f * (1 - cosTheta); PluginManager::getInstance()->createObject(
MTS_CLASS(Emitter), props));
} else {
size_t pixelCount = resolution*resolution/2;
Float cosTheta = std::cos(theta * sunRadiusScale);
/* Approx. number of samples that need to be generated, /* Ratio of the sphere that is covered by the sun */
be very conservative */ Float coveredPortion = 0.5f * (1 - cosTheta);
size_t nSamples = (size_t) std::max((Float) 100,
(pixelCount * coveredPortion * 1000));
factor = Point2(bitmap->getWidth() / (2*M_PI), /* Approx. number of samples that need to be generated,
bitmap->getHeight() / M_PI); be very conservative */
size_t nSamples = (size_t) std::max((Float) 100,
(pixelCount * coveredPortion * 1000));
Spectrum value = factor = Point2(bitmap->getWidth() / (2*M_PI),
sunRadiance * (2 * M_PI * (1-std::cos(theta))) * bitmap->getHeight() / M_PI);
(bitmap->getWidth() * bitmap->getHeight())
/ (2.0f * M_PI * M_PI * (Float) nSamples);
for (size_t i=0; i<nSamples; ++i) { Spectrum value =
Vector dir = sunFrame.toWorld( sunRadiance * (2 * M_PI * (1-std::cos(theta))) *
Warp::squareToUniformCone(cosTheta, sample02(i))); (bitmap->getWidth() * bitmap->getHeight())
/ (2.0f * M_PI * M_PI * (Float) nSamples);
Float sinTheta = math::safe_sqrt(1-dir.y*dir.y); for (size_t i=0; i<nSamples; ++i) {
SphericalCoordinates sphCoords = fromSphere(dir); Vector dir = sunFrame.toWorld(
Warp::squareToUniformCone(cosTheta, sample02(i)));
Point2i pos( Float sinTheta = math::safe_sqrt(1-dir.y*dir.y);
std::min(std::max(0, (int) (sphCoords.azimuth * factor.x)), bitmap->getWidth()-1), SphericalCoordinates sphCoords = fromSphere(dir);
std::min(std::max(0, (int) (sphCoords.elevation * factor.y)), bitmap->getHeight()-1));
Point2i pos(
std::min(std::max(0, (int) (sphCoords.azimuth * factor.x)), bitmap->getWidth()-1),
std::min(std::max(0, (int) (sphCoords.elevation * factor.y)), bitmap->getHeight()-1));
data[pos.x + pos.y * bitmap->getWidth()] += value / std::max((Float) 1e-3f, sinTheta);
}
data[pos.x + pos.y * bitmap->getWidth()] += value / std::max((Float) 1e-3f, sinTheta);
} }
Log(EDebug, "Done (took %i ms)", timer->getMilliseconds()); Log(EDebug, "Done (took %i ms)", timer->getMilliseconds());
@ -204,7 +225,7 @@ public:
envProps.setData("bitmap", bitmapData); envProps.setData("bitmap", bitmapData);
envProps.setTransform("toWorld", trafo); envProps.setTransform("toWorld", trafo);
envProps.setFloat("samplingWeight", m_samplingWeight); envProps.setFloat("samplingWeight", m_samplingWeight);
m_emitter = static_cast<Emitter *>( m_envEmitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(
MTS_CLASS(Emitter), envProps)); MTS_CLASS(Emitter), envProps));
@ -217,17 +238,24 @@ public:
SunSkyEmitter(Stream *stream, InstanceManager *manager) SunSkyEmitter(Stream *stream, InstanceManager *manager)
: Emitter(stream, manager) { : Emitter(stream, manager) {
m_emitter = static_cast<Emitter *>(manager->getInstance(stream)); m_envEmitter = static_cast<Emitter *>(manager->getInstance(stream));
if (stream->readBool())
m_dirEmitter = static_cast<Emitter *>(manager->getInstance(stream));
} }
void serialize(Stream *stream, InstanceManager *manager) const { void serialize(Stream *stream, InstanceManager *manager) const {
Emitter::serialize(stream, manager); Emitter::serialize(stream, manager);
manager->serialize(stream, m_emitter.get()); manager->serialize(stream, m_envEmitter.get());
stream->writeBool(m_dirEmitter.get() != NULL);
if (m_dirEmitter.get())
manager->serialize(stream, m_dirEmitter.get());
} }
void configure() { void configure() {
Emitter::configure(); Emitter::configure();
m_emitter->configure(); m_envEmitter->configure();
if (m_dirEmitter)
m_dirEmitter->configure();
} }
bool isCompound() const { bool isCompound() const {
@ -240,14 +268,17 @@ public:
Emitter *getElement(size_t i) { Emitter *getElement(size_t i) {
if (i == 0) if (i == 0)
return m_emitter; return m_envEmitter;
else if (i == 1)
return m_dirEmitter;
else else
return NULL; return NULL;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
ref<Emitter> m_emitter; ref<Emitter> m_dirEmitter;
ref<Emitter> m_envEmitter;
}; };
MTS_IMPLEMENT_CLASS_S(SunSkyEmitter, false, Emitter) MTS_IMPLEMENT_CLASS_S(SunSkyEmitter, false, Emitter)

View File

@ -643,7 +643,11 @@ bool SpecularManifold::update(Path &path, int start, int end) {
step = -1; mode = ERadiance; step = -1; mode = ERadiance;
} }
for (int j=0, i=start; j < (int) m_vertices.size()-2; ++j, i += step) { int last = (int) m_vertices.size() - 2;
if (m_vertices[0].type == EPinnedDirection)
last = std::max(last, 1);
for (int j=0, i=start; j < last; ++j, i += step) {
const SimpleVertex const SimpleVertex
&v = m_vertices[j], &v = m_vertices[j],
&vn = m_vertices[j+1]; &vn = m_vertices[j+1];
@ -664,7 +668,8 @@ bool SpecularManifold::update(Path &path, int start, int end) {
PathVertex::EMediumInteraction : PathVertex::ESurfaceInteraction; PathVertex::EMediumInteraction : PathVertex::ESurfaceInteraction;
if (v.type == EPinnedDirection) { if (v.type == EPinnedDirection) {
/* Create a fake vertex and use it to call sampleDirect() */ /* Create a fake vertex and use it to call sampleDirect(). This is
kind of terrible -- a nicer API is needed to cleanly support this */
PathVertex temp; PathVertex temp;
temp.type = PathVertex::EMediumInteraction; temp.type = PathVertex::EMediumInteraction;
temp.degenerate = false; temp.degenerate = false;
@ -681,7 +686,7 @@ bool SpecularManifold::update(Path &path, int start, int end) {
return false; return false;
} }
if (m_vertices.size() > 3) { if (m_vertices.size() >= 3) {
PathVertex *succ2 = path.vertex(i+2*step); PathVertex *succ2 = path.vertex(i+2*step);
PathEdge *succ2Edge = path.edge(predEdgeIdx + 2*step); PathEdge *succ2Edge = path.edge(predEdgeIdx + 2*step);
if (!succ->sampleNext(m_scene, NULL, vertex, succEdge, succ2Edge, succ2, mode)) { if (!succ->sampleNext(m_scene, NULL, vertex, succEdge, succ2Edge, succ2, mode)) {
@ -863,37 +868,30 @@ Float SpecularManifold::det(const Path &path, int a, int b, int c) {
} }
Float SpecularManifold::multiG(const Path &path, int a, int b) { Float SpecularManifold::multiG(const Path &path, int a, int b) {
if (a == 0) { if (a == 0)
++a; ++a;
if (!path.vertex(a)->isConnectable()) else if (a == path.length())
++a;
} else if (a == path.length()) {
--a; --a;
if (!path.vertex(a)->isConnectable()) if (b == 0)
--a;
}
if (b == 0) {
++b; ++b;
if (!path.vertex(b)->isConnectable()) else if (b == path.length())
++b;
} else if (b == path.length()) {
--b; --b;
if (!path.vertex(b)->isConnectable())
--b;
}
int step = b > a ? 1 : -1;
while (!path.vertex(b)->isConnectable())
b -= step;
while (!path.vertex(a)->isConnectable())
a += step;
int step = b > a ? 1 : -1, start = a;
Float result = 1; Float result = 1;
BDAssert(path.vertex(a)->isConnectable() && path.vertex(b)->isConnectable()); BDAssert(path.vertex(a)->isConnectable() && path.vertex(b)->isConnectable());
for (int i = a + step; i != b + step; i += step) { for (int i = a + step, start = a; i != b + step; i += step) {
if (path.vertex(i)->isConnectable()) { if (path.vertex(i)->isConnectable()) {
result *= G(path, start, i); result *= G(path, start, i);
start = i; start = i;
} }
} }
BDAssert(start == b);
return result; return result;
} }

View File

@ -272,8 +272,10 @@ bool ManifoldPerturbation::sampleMutation(
for (int i=l+1; i<m; ++i) { for (int i=l+1; i<m; ++i) {
proposal.append(m_pool.allocVertex()); proposal.append(m_pool.allocVertex());
proposal.append(m_pool.allocEdge()); proposal.append(m_pool.allocEdge());
memset(proposal.vertex(proposal.vertexCount()-1), 0, sizeof(PathVertex)); /// XXX
} }
proposal.append(source, m, k+1); proposal.append(source, m, k+1);
proposal.vertex(a) = proposal.vertex(a)->clone(m_pool); proposal.vertex(a) = proposal.vertex(a)->clone(m_pool);
proposal.vertex(c) = proposal.vertex(c)->clone(m_pool); proposal.vertex(c) = proposal.vertex(c)->clone(m_pool);
@ -521,8 +523,8 @@ bool ManifoldPerturbation::sampleMutation(
} }
} }
if ((vb_old->isSurfaceInteraction() && m_thetaDiffSurfaceSamples < DIFF_SAMPLES) || if (((vb_old->isSurfaceInteraction() && m_thetaDiffSurfaceSamples < DIFF_SAMPLES) ||
(vb_old->isMediumInteraction() && m_thetaDiffMediumSamples < DIFF_SAMPLES)) { (vb_old->isMediumInteraction() && m_thetaDiffMediumSamples < DIFF_SAMPLES)) && b+1 != k && b-1 != 0) {
LockGuard guard(m_thetaDiffMutex); LockGuard guard(m_thetaDiffMutex);
if ((vb_old->isSurfaceInteraction() && m_thetaDiffSurfaceSamples < DIFF_SAMPLES) || if ((vb_old->isSurfaceInteraction() && m_thetaDiffSurfaceSamples < DIFF_SAMPLES) ||
@ -586,6 +588,7 @@ bool ManifoldPerturbation::sampleMutation(
} }
} }
} }
if (!PathVertex::connect(m_scene, if (!PathVertex::connect(m_scene,
proposal.vertexOrNull(q-1), proposal.vertexOrNull(q-1),
proposal.edgeOrNull(q-1), proposal.edgeOrNull(q-1),

View File

@ -580,7 +580,9 @@ Float PathSampler::computeAverageLuminance(size_t sampleCount) {
} }
static void seedCallback(std::vector<PathSeed> &output, const Bitmap *importanceMap, static void seedCallback(std::vector<PathSeed> &output, const Bitmap *importanceMap,
int s, int t, Float weight, Path &path) { Float &accum, int s, int t, Float weight, Path &path) {
accum += weight;
if (importanceMap) { if (importanceMap) {
const Float *luminanceValues = importanceMap->getFloatData(); const Float *luminanceValues = importanceMap->getFloatData();
Vector2i size = importanceMap->getSize(); Vector2i size = importanceMap->getSize();
@ -608,40 +610,40 @@ Float PathSampler::generateSeeds(size_t sampleCount, size_t seedCount,
tempSeeds.reserve(sampleCount); tempSeeds.reserve(sampleCount);
SplatList splatList; SplatList splatList;
Float luminance;
PathCallback callback = boost::bind(&seedCallback, PathCallback callback = boost::bind(&seedCallback,
boost::ref(tempSeeds), importanceMap, _1, _2, _3, _4); boost::ref(tempSeeds), importanceMap, boost::ref(luminance),
_1, _2, _3, _4);
Float mean = 0.0f, variance = 0.0f; Float mean = 0.0f, variance = 0.0f;
for (size_t i=0; i<sampleCount; ++i) { for (size_t i=0; i<sampleCount; ++i) {
size_t seedIndex = tempSeeds.size(); size_t seedIndex = tempSeeds.size();
size_t sampleIndex = m_sensorSampler->getSampleIndex(); size_t sampleIndex = m_sensorSampler->getSampleIndex();
Float lum = 0.0f; luminance = 0.0f;
if (fineGrained) { if (fineGrained) {
samplePaths(Point2i(-1), callback); samplePaths(Point2i(-1), callback);
/* Fine seed granularity (e.g. for Veach-MLT). /* Fine seed granularity (e.g. for Veach-MLT).
Set the correct the sample index value */ Set the correct the sample index value */
for (size_t j = seedIndex; j<tempSeeds.size(); ++j) { for (size_t j = seedIndex; j<tempSeeds.size(); ++j)
tempSeeds[j].sampleIndex = sampleIndex; tempSeeds[j].sampleIndex = sampleIndex;
lum += tempSeeds[j].luminance;
}
} else { } else {
/* Run the path sampling strategy */ /* Run the path sampling strategy */
sampleSplats(Point2i(-1), splatList); sampleSplats(Point2i(-1), splatList);
luminance = splatList.luminance;
splatList.normalize(importanceMap); splatList.normalize(importanceMap);
lum = splatList.luminance;
/* Coarse seed granularity (e.g. for PSSMLT) */ /* Coarse seed granularity (e.g. for PSSMLT) */
if (lum != 0) if (luminance != 0)
tempSeeds.push_back(PathSeed(sampleIndex, lum)); tempSeeds.push_back(PathSeed(sampleIndex, luminance));
} }
/* Numerically robust online variance estimation using an /* Numerically robust online variance estimation using an
algorithm proposed by Donald Knuth (TAOCP vol.2, 3rd ed., p.232) */ algorithm proposed by Donald Knuth (TAOCP vol.2, 3rd ed., p.232) */
Float delta = lum - mean; Float delta = luminance - mean;
mean += delta / (Float) (i+1); mean += delta / (Float) (i+1);
variance += delta * (lum - mean); variance += delta * (luminance - mean);
} }
BDAssert(m_pool.unused()); BDAssert(m_pool.unused());
Float stddev = std::sqrt(variance / (sampleCount-1)); Float stddev = std::sqrt(variance / (sampleCount-1));

View File

@ -158,10 +158,11 @@ void Scheduler::retainResource(int id) {
rec->refCount++; rec->refCount++;
} }
void Scheduler::unregisterResource(int id) { bool Scheduler::unregisterResource(int id) {
LockGuard lock(m_mutex); LockGuard lock(m_mutex);
if (m_resources.find(id) == m_resources.end()) { if (m_resources.find(id) == m_resources.end()) {
Log(EError, "unregisterResource(): could not find the resource with ID %i!", id); Log(EWarn, "unregisterResource(): could not find the resource with ID %i!", id);
return false;
} }
ResourceRecord *rec = m_resources[id]; ResourceRecord *rec = m_resources[id];
if (--rec->refCount == 0) { if (--rec->refCount == 0) {
@ -175,6 +176,7 @@ void Scheduler::unregisterResource(int id) {
for (size_t i=0; i<m_workers.size(); ++i) for (size_t i=0; i<m_workers.size(); ++i)
m_workers[i]->signalResourceExpiration(id); m_workers[i]->signalResourceExpiration(id);
} }
return true;
} }
SerializableObject *Scheduler::getResource(int id, int coreIndex) { SerializableObject *Scheduler::getResource(int id, int coreIndex) {

View File

@ -16,6 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if defined(__GXX_EXPERIMENTAL_CXX0X__)
/* Needed to prevent a segmentation fault in the Intel C++
compiler on Linux (as of Nov 2012) */
#undef __GXX_EXPERIMENTAL_CXX0X__
#endif
#if MTS_SSE #if MTS_SSE
#include <mitsuba/mitsuba.h> #include <mitsuba/mitsuba.h>
#include <mitsuba/core/ssemath.h> #include <mitsuba/core/ssemath.h>

View File

@ -134,7 +134,7 @@ void Statistics::logPlugin(const std::string &name, const std::string &descr) {
} }
void Statistics::printStats() { void Statistics::printStats() {
SLog(EInfo, "Statistics: \n%s", getStats().c_str()); SLog(EInfo, "Statistics:\n%s", getStats().c_str());
} }
std::string Statistics::getStats() { std::string Statistics::getStats() {

View File

@ -469,9 +469,9 @@ bool solveLinearSystem2x2(const Float a[2][2], const Float b[2], Float x[2]) {
return true; return true;
} }
Float interpCubic1D(Float x, const Float *data, Float min, Float max, size_t size) { Float interpCubic1D(Float x, const Float *data, Float min, Float max, size_t size, bool extrapolate) {
/* Give up when given an out-of-range or NaN argument */ /* Give up when given an out-of-range or NaN argument */
if (!(x >= min && x <= max)) if (!(x >= min && x <= max) && !extrapolate)
return 0.0f; return 0.0f;
/* Transform 'x' so that knots lie at integer positions */ /* Transform 'x' so that knots lie at integer positions */
@ -508,9 +508,9 @@ Float interpCubic1D(Float x, const Float *data, Float min, Float max, size_t siz
( t3 - t2) * d1; ( t3 - t2) * d1;
} }
Float interpCubic1DIrregular(Float x, const Float *nodes, const Float *data, size_t size) { Float interpCubic1DIrregular(Float x, const Float *nodes, const Float *data, size_t size, bool extrapolate) {
/* Give up when given an out-of-range or NaN argument */ /* Give up when given an out-of-range or NaN argument */
if (!(x >= nodes[0] && x <= nodes[size-1])) if (!(x >= nodes[0] && x <= nodes[size-1]) && !extrapolate)
return 0.0f; return 0.0f;
size_t k = (size_t) std::max((ptrdiff_t) 0, std::min((ptrdiff_t) size - 2, size_t k = (size_t) std::max((ptrdiff_t) 0, std::min((ptrdiff_t) size - 2,
@ -545,7 +545,7 @@ Float interpCubic1DIrregular(Float x, const Float *nodes, const Float *data, siz
Float interpCubic2D(const Point2 &p, const Float *data, Float interpCubic2D(const Point2 &p, const Float *data,
const Point2 &min, const Point2 &max, const Size2 &size) { const Point2 &min, const Point2 &max, const Size2 &size, bool extrapolate) {
Float knotWeights[2][4]; Float knotWeights[2][4];
Size2 knot; Size2 knot;
@ -553,7 +553,7 @@ Float interpCubic2D(const Point2 &p, const Float *data,
for (int dim=0; dim<2; ++dim) { for (int dim=0; dim<2; ++dim) {
Float *weights = knotWeights[dim]; Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */ /* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= min[dim] && p[dim] <= max[dim])) if (!(p[dim] >= min[dim] && p[dim] <= max[dim]) && !extrapolate)
return 0.0f; return 0.0f;
/* Transform 'p' so that knots lie at integer positions */ /* Transform 'p' so that knots lie at integer positions */
@ -615,7 +615,7 @@ Float interpCubic2D(const Point2 &p, const Float *data,
} }
Float interpCubic2DIrregular(const Point2 &p, const Float **nodes_, Float interpCubic2DIrregular(const Point2 &p, const Float **nodes_,
const Float *data, const Size2 &size) { const Float *data, const Size2 &size, bool extrapolate) {
Float knotWeights[2][4]; Float knotWeights[2][4];
Size2 knot; Size2 knot;
@ -625,7 +625,7 @@ Float interpCubic2DIrregular(const Point2 &p, const Float **nodes_,
Float *weights = knotWeights[dim]; Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */ /* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= nodes[0] && p[dim] <= nodes[size[dim]-1])) if (!(p[dim] >= nodes[0] && p[dim] <= nodes[size[dim]-1]) && !extrapolate)
return 0.0f; return 0.0f;
/* Find the index of the left knot in the queried subinterval, be /* Find the index of the left knot in the queried subinterval, be
@ -689,7 +689,7 @@ Float interpCubic2DIrregular(const Point2 &p, const Float **nodes_,
} }
Float interpCubic3D(const Point3 &p, const Float *data, Float interpCubic3D(const Point3 &p, const Float *data,
const Point3 &min, const Point3 &max, const Size3 &size) { const Point3 &min, const Point3 &max, const Size3 &size, bool extrapolate) {
Float knotWeights[3][4]; Float knotWeights[3][4];
Size3 knot; Size3 knot;
@ -697,7 +697,7 @@ Float interpCubic3D(const Point3 &p, const Float *data,
for (int dim=0; dim<3; ++dim) { for (int dim=0; dim<3; ++dim) {
Float *weights = knotWeights[dim]; Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */ /* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= min[dim] && p[dim] <= max[dim])) if (!(p[dim] >= min[dim] && p[dim] <= max[dim]) && !extrapolate)
return 0.0f; return 0.0f;
/* Transform 'p' so that knots lie at integer positions */ /* Transform 'p' so that knots lie at integer positions */
@ -763,7 +763,7 @@ Float interpCubic3D(const Point3 &p, const Float *data,
} }
Float interpCubic3DIrregular(const Point3 &p, const Float **nodes_, Float interpCubic3DIrregular(const Point3 &p, const Float **nodes_,
const Float *data, const Size3 &size) { const Float *data, const Size3 &size, bool extrapolate) {
Float knotWeights[3][4]; Float knotWeights[3][4];
Size3 knot; Size3 knot;
@ -773,7 +773,7 @@ Float interpCubic3DIrregular(const Point3 &p, const Float **nodes_,
Float *weights = knotWeights[dim]; Float *weights = knotWeights[dim];
/* Give up when given an out-of-range or NaN argument */ /* Give up when given an out-of-range or NaN argument */
if (!(p[dim] >= nodes[0] && p[dim] <= nodes[size[dim]-1])) if (!(p[dim] >= nodes[0] && p[dim] <= nodes[size[dim]-1]) && !extrapolate)
return 0.0f; return 0.0f;
/* Find the index of the left knot in the queried subinterval, be /* Find the index of the left knot in the queried subinterval, be

View File

@ -542,7 +542,7 @@ void TriMesh::rebuildTopology(Float maxAngle) {
configure(); configure();
} }
void TriMesh::computeNormals() { void TriMesh::computeNormals(bool force) {
int invalidNormals = 0; int invalidNormals = 0;
if (m_faceNormals) { if (m_faceNormals) {
if (m_normals) { if (m_normals) {
@ -558,7 +558,7 @@ void TriMesh::computeNormals() {
} }
} }
} else { } else {
if (m_normals) { if (m_normals && !force) {
if (m_flipNormals) { if (m_flipNormals) {
for (size_t i=0; i<m_vertexCount; i++) for (size_t i=0; i<m_vertexCount; i++)
m_normals[i] *= -1; m_normals[i] *= -1;
@ -566,7 +566,8 @@ void TriMesh::computeNormals() {
/* Do nothing */ /* Do nothing */
} }
} else { } else {
m_normals = new Normal[m_vertexCount]; if (!m_normals)
m_normals = new Normal[m_vertexCount];
memset(m_normals, 0, sizeof(Normal)*m_vertexCount); memset(m_normals, 0, sizeof(Normal)*m_vertexCount);
/* Well-behaved vertex normal computation based on /* Well-behaved vertex normal computation based on
@ -617,7 +618,7 @@ void TriMesh::computeNormals() {
} }
void TriMesh::computeUVTangents() { void TriMesh::computeUVTangents() {
int degenerate = 0; // int degenerate = 0;
if (!m_texcoords) { if (!m_texcoords) {
bool anisotropic = hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropic; bool anisotropic = hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropic;
if (anisotropic) if (anisotropic)
@ -654,7 +655,7 @@ void TriMesh::computeUVTangents() {
Normal n = Normal(cross(dP1, dP2)); Normal n = Normal(cross(dP1, dP2));
Float length = n.length(); Float length = n.length();
if (length == 0) { if (length == 0) {
++degenerate; // ++degenerate;
continue; continue;
} }
@ -670,9 +671,12 @@ void TriMesh::computeUVTangents() {
} }
} }
if (degenerate > 0) #if 0
Log(EWarn, "\"%s\": computeTangentSpace(): Mesh contains %i " /* Don't be so noisy -- this isn't usually a problem.. */
"degenerate triangles!", getName().c_str(), degenerate); if (degenerate > 0)
Log(EWarn, "\"%s\": computeTangentSpace(): Mesh contains %i "
"degenerate triangles!", getName().c_str(), degenerate);
#endif
} }
void TriMesh::getNormalDerivative(const Intersection &its, void TriMesh::getNormalDerivative(const Intersection &its,

View File

@ -1593,7 +1593,7 @@ void MainWindow::on_actionStartServer_triggered() {
void MainWindow::on_actionEnableCommandLine_triggered() { void MainWindow::on_actionEnableCommandLine_triggered() {
if (QMessageBox::question(this, tr("Enable command line access"), if (QMessageBox::question(this, tr("Enable command line access"),
tr("<p>If you proceed, Mitsuba will create symbolic links in <tt>/usr/bin</tt> and <tt>/Library/Python/{2.6,2.7}/site-packages</tt>, " tr("<p>If you proceed, Mitsuba will create symbolic links in <tt>/usr/bin</tt> and <tt>/Library/Python/{2.6,2.7}/site-packages</tt>, as well as an entry in .bashrc, "
"which enable command line and Python usage. Note that you will have to " "which enable command line and Python usage. Note that you will have to "
"repeat this process every time the Mitsuba application is moved.</p>" "repeat this process every time the Mitsuba application is moved.</p>"
"<p>Create links?</p>"), "<p>Create links?</p>"),

View File

@ -4,6 +4,7 @@
#include <AuthorizationTags.h> #include <AuthorizationTags.h>
#include <unistd.h> #include <unistd.h>
#include <iostream> #include <iostream>
#include <sstream>
namespace mitsuba { namespace mitsuba {
extern std::string __mts_bundlepath(); extern std::string __mts_bundlepath();
@ -30,7 +31,10 @@ bool create_symlinks() {
} }
std::string bundlePath = mitsuba::__mts_bundlepath(); std::string bundlePath = mitsuba::__mts_bundlepath();
std::string path = bundlePath + "/Contents/MacOS/symlinks_install"; std::string path = bundlePath + "/Contents/MacOS/symlinks_install";
char *args[] = { const_cast<char *>(bundlePath.c_str()), NULL }; std::ostringstream oss;
oss << getuid();
std::string uid = oss.str();
char *args[] = { const_cast<char *>(bundlePath.c_str()), const_cast<char *>(uid.c_str()), NULL };
FILE *pipe = NULL; FILE *pipe = NULL;
flags = kAuthorizationFlagDefaults; flags = kAuthorizationFlagDefaults;
status = AuthorizationExecuteWithPrivileges(ref, const_cast<char *>(path.c_str()), flags, args, &pipe); status = AuthorizationExecuteWithPrivileges(ref, const_cast<char *>(path.c_str()), flags, args, &pipe);

View File

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h> #include <sys/param.h>
#include <pwd.h>
void installPython(const char *basedir, const char *version) { void installPython(const char *basedir, const char *version) {
char fname[MAXPATHLEN]; char fname[MAXPATHLEN];
@ -23,6 +24,22 @@ void installPython(const char *basedir, const char *version) {
fclose(f); fclose(f);
} }
void appendShellConfig(const char *basedir, const char *target, const char *fmt, const char *dir) {
char fname[MAXPATHLEN];
snprintf(fname, sizeof(fname), "%s/%s", basedir, target);
if (access(fname, R_OK) < 0)
return;
FILE *f = fopen(fname, "a");
if (!f)
return;
fprintf(f, fmt, dir);
fclose(f);
}
void install(const char *basedir, const char *name) { void install(const char *basedir, const char *name) {
char fname[MAXPATHLEN]; char fname[MAXPATHLEN];
FILE *f; FILE *f;
@ -51,7 +68,7 @@ void install(const char *basedir, const char *name) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc != 2) { if (argc != 3) {
fprintf(stderr, "Incorrect number of arguments!\n"); fprintf(stderr, "Incorrect number of arguments!\n");
return -1; return -1;
} }
@ -68,6 +85,11 @@ int main(int argc, char **argv) {
install(argv[1], "mtsimport"); install(argv[1], "mtsimport");
installPython(argv[1], "2.6"); installPython(argv[1], "2.6");
installPython(argv[1], "2.7"); installPython(argv[1], "2.7");
struct passwd *pw = getpwuid(atoi(argv[2]));
appendShellConfig(pw->pw_dir, ".bashrc", "\nexport LD_LIBRARY_PATH=%s/Contents/Frameworks:$LD_LIBRARY_PATH\n", argv[1]);
appendShellConfig(pw->pw_dir, ".zshrc", "\nexport LD_LIBRARY_PATH=%s/Contents/Frameworks:$LD_LIBRARY_PATH\n", argv[1]);
appendShellConfig(pw->pw_dir, ".cshrc", "\nsetenv LD_LIBRARY_PATH %s/Contents/Frameworks:${LD_LIBRARY_PATH}\n", argv[1]);
return 0; return 0;
} }

View File

@ -510,8 +510,8 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "Cylinder[" << endl oss << "Cylinder[" << endl
<< " radius = " << m_radius << ", " << endl << " radius = " << m_radius << "," << endl
<< " length = " << m_length << ", " << endl << " length = " << m_length << "," << endl
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl << " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl; << " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
if (isMediumTransition()) if (isMediumTransition())

View File

@ -261,7 +261,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "Disk[" << endl oss << "Disk[" << endl
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << ", " << endl << " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl; << " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
if (isMediumTransition()) { if (isMediumTransition()) {
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl

View File

@ -39,7 +39,6 @@ MTS_NAMESPACE_BEGIN
Instance::Instance(const Properties &props) : Shape(props) { Instance::Instance(const Properties &props) : Shape(props) {
m_objectToWorld = props.getTransform("toWorld", Transform()); m_objectToWorld = props.getTransform("toWorld", Transform());
m_worldToObject = m_objectToWorld.inverse(); m_worldToObject = m_objectToWorld.inverse();
m_invScale = 1.0f/m_objectToWorld(Vector(0, 0, 1)).length();
} }
Instance::Instance(Stream *stream, InstanceManager *manager) Instance::Instance(Stream *stream, InstanceManager *manager)
@ -47,14 +46,12 @@ Instance::Instance(Stream *stream, InstanceManager *manager)
m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream)); m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream));
m_objectToWorld = Transform(stream); m_objectToWorld = Transform(stream);
m_worldToObject = m_objectToWorld.inverse(); m_worldToObject = m_objectToWorld.inverse();
m_invScale = stream->readFloat();
} }
void Instance::serialize(Stream *stream, InstanceManager *manager) const { void Instance::serialize(Stream *stream, InstanceManager *manager) const {
Shape::serialize(stream, manager); Shape::serialize(stream, manager);
manager->serialize(stream, m_shapeGroup.get()); manager->serialize(stream, m_shapeGroup.get());
m_objectToWorld.serialize(stream); m_objectToWorld.serialize(stream);
stream->writeFloat(m_invScale);
} }
void Instance::configure() { void Instance::configure() {
@ -130,17 +127,26 @@ void Instance::fillIntersectionRecord(const Ray &_ray,
void Instance::getNormalDerivative(const Intersection &its, void Instance::getNormalDerivative(const Intersection &its,
Vector &dndu, Vector &dndv, bool shadingFrame) const { Vector &dndu, Vector &dndv, bool shadingFrame) const {
/// TODO: this is horrible /* The following is really super-inefficient, but it's
needed to be able to deal with general transformations */
Intersection temp(its); Intersection temp(its);
temp.p = m_worldToObject(its.p); temp.p = m_worldToObject(its.p);
temp.dpdu = m_worldToObject(its.dpdu); temp.dpdu = m_worldToObject(its.dpdu);
temp.dpdv = m_worldToObject(its.dpdv); temp.dpdv = m_worldToObject(its.dpdv);
/* Determine the length of the transformed normal
*before* it was re-normalized */
Normal tn = m_objectToWorld(normalize(m_worldToObject(its.shFrame.n)));
Float invLen = 1/tn.length();
tn *= invLen;
its.shape->getNormalDerivative(temp, dndu, dndv, shadingFrame); its.shape->getNormalDerivative(temp, dndu, dndv, shadingFrame);
/* The following will probably be incorrect for dndu = m_objectToWorld(Normal(dndu)) * invLen;
non-rigid transformations */ dndv = m_objectToWorld(Normal(dndv)) * invLen;
dndu = m_objectToWorld(Normal(dndu))*m_invScale;
dndv = m_objectToWorld(Normal(dndv))*m_invScale; dndu -= tn * dot(tn, dndu);
dndv -= tn * dot(tn, dndv);
} }
MTS_IMPLEMENT_CLASS_S(Instance, false, Shape) MTS_IMPLEMENT_CLASS_S(Instance, false, Shape)

View File

@ -81,7 +81,6 @@ public:
private: private:
ref<ShapeGroup> m_shapeGroup; ref<ShapeGroup> m_shapeGroup;
Transform m_objectToWorld, m_worldToObject; Transform m_objectToWorld, m_worldToObject;
Float m_invScale;
}; };
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -229,7 +229,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "Rectangle[" << endl oss << "Rectangle[" << endl
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << ", " << endl; << " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl;
if (isMediumTransition()) if (isMediumTransition())
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl
<< " exteriorMedium = " << indent(m_exteriorMedium.toString()) << "," << endl; << " exteriorMedium = " << indent(m_exteriorMedium.toString()) << "," << endl;

View File

@ -170,7 +170,7 @@ size_t ShapeGroup::getEffectivePrimitiveCount() const {
std::string ShapeGroup::toString() const { std::string ShapeGroup::toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "ShapeGroup[" << endl oss << "ShapeGroup[" << endl
<< " name = \"" << m_name << "\", " << endl << " name = \"" << m_name << "\"," << endl
<< " primCount = " << m_kdtree->getPrimitiveCount() << endl << " primCount = " << m_kdtree->getPrimitiveCount() << endl
<< "]"; << "]";
return oss.str(); return oss.str();

View File

@ -468,8 +468,8 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "Sphere[" << endl oss << "Sphere[" << endl
<< " radius = " << m_radius << ", " << endl << " radius = " << m_radius << "," << endl
<< " center = " << m_center.toString() << ", " << endl << " center = " << m_center.toString() << "," << endl
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl; << " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
if (isMediumTransition()) if (isMediumTransition())
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl