merge
commit
f66737cf39
1
.hgtags
1
.hgtags
|
@ -6,3 +6,4 @@ e3c0182ba64b77319ce84c9e2a8581649e68273d v0.2.1
|
|||
cb6e89af8012fac22cc0f3c5ad247c98c701bdda v0.3.0
|
||||
ee26517b27207353b0c8a7d357bcb4977b5d93fb v0.4.0
|
||||
7db07694ea00eb1655f7a1adcc3ae880e8e116f9 v0.4.1
|
||||
13a39b11aceee517c19d2e2cec2e6b875546062c v0.4.2
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
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/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
|
||||
|
|
|
@ -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
|
||||
* negative pixel values in textures and environment maps are handled more gracefully.
|
||||
* minor robustness improvements to the OBJ and COLLADA importers.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: mitsuba
|
||||
Version: 0.4.1
|
||||
Version: 0.4.2
|
||||
Release: 1%{?dist}
|
||||
Summary: Mitsuba renderer
|
||||
Group: Applications/Graphics
|
||||
|
@ -62,6 +62,9 @@ rm -rf $RPM_BUILD_ROOT
|
|||
/usr/include/*
|
||||
%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}
|
||||
- Upgrade to version 0.4.1
|
||||
|
||||
|
|
|
@ -129,9 +129,8 @@ scons: $\texttt{done}$ building targets.
|
|||
\end{shell}
|
||||
To run the renderer from the command line, you first have to import it into your shell environment:
|
||||
\begin{shell}
|
||||
$\text{\$}$ . setpath.sh
|
||||
$\text{\$}$ source setpath.sh
|
||||
\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}.
|
||||
\subsubsection{Creating Debian or Ubuntu Linux packages}
|
||||
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}
|
||||
To run the renderer from the command line, you first have to import it into your shell environment:
|
||||
\begin{shell}
|
||||
$\text{\$}$ . setpath.sh
|
||||
$\text{\$}$ source setpath.sh
|
||||
\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}.
|
||||
\subsubsection{Creating Fedora Core packages}
|
||||
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}
|
||||
To run the renderer from the command line, you first have to import it into your shell environment:
|
||||
\begin{shell}
|
||||
$\text{\$}$ . setpath.sh
|
||||
$\text{\$}$ source setpath.sh
|
||||
\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}.
|
||||
\subsubsection{Creating Arch Linux packages}
|
||||
Mitsuba ships with a \code{PKGBUILD} file, which automatically builds
|
||||
|
@ -350,7 +348,6 @@ scons: $\texttt{done}$ building targets.
|
|||
\end{shell}
|
||||
To run the renderer from the command line, you first have to import it into your shell environment:
|
||||
\begin{shell}
|
||||
$\text{\$}$ . setpath.sh
|
||||
$\text{\$}$ source setpath.sh
|
||||
\end{shell}
|
||||
(note the period at the beginning -- this assumes that you are using \code{bash}).
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -38,12 +38,12 @@ In Mitsuba's \code{src/integrators} directory, create a file named
|
|||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
class MyIntegrator : public SampleIntegrator {
|
||||
class MyIntegrator : public SamplingIntegrator {
|
||||
public:
|
||||
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_NAMESPACE_END
|
||||
\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 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
|
||||
\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}
|
||||
public:
|
||||
/// Initialize the integrator with the specified properties
|
||||
MyIntegrator(const Properties &props) : SampleIntegrator(props) {
|
||||
MyIntegrator(const Properties &props) : SamplingIntegrator(props) {
|
||||
Spectrum defaultColor;
|
||||
defaultColor.fromLinearRGB(0.2f, 0.5f, 0.2f);
|
||||
m_color = props.getSpectrum("color", defaultColor);
|
||||
|
@ -106,13 +106,13 @@ Next, we need to add serialization and unserialization support:
|
|||
\begin{cpp}
|
||||
/// Unserialize from a binary data stream
|
||||
MyIntegrator(Stream *stream, InstanceManager *manager)
|
||||
: SampleIntegrator(stream, manager) {
|
||||
: SamplingIntegrator(stream, manager) {
|
||||
m_color = Spectrum(stream);
|
||||
}
|
||||
|
||||
/// Serialize to a binary data stream
|
||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||
SampleIntegrator::serialize(stream, manager);
|
||||
SamplingIntegrator::serialize(stream, manager);
|
||||
m_color.serialize(stream);
|
||||
}
|
||||
\end{cpp}
|
||||
|
@ -205,11 +205,12 @@ we can override the \code{preprocess} function:
|
|||
bool preprocess(const Scene *scene, RenderQueue *queue,
|
||||
const RenderJob *job, int sceneResID, int cameraResID,
|
||||
int samplerResID) {
|
||||
SampleIntegrator::preprocess(scene, queue, job, sceneResID,
|
||||
SamplingIntegrator::preprocess(scene, queue, job, sceneResID,
|
||||
cameraResID, samplerResID);
|
||||
|
||||
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();
|
||||
|
||||
for (int i=0; i<8; ++i)
|
||||
|
@ -276,7 +277,7 @@ into a scene XML file:
|
|||
\end{xml}
|
||||
To support this kind of complex interaction, some information needs to be passed between the
|
||||
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:
|
||||
the reason why we didn't just do this by calling
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
\setcounter{secnumdepth}{3}
|
||||
\setcounter{tocdepth}{3}
|
||||
|
||||
\newcommand{\MitsubaVersion}{0.4.1}
|
||||
\newcommand{\MitsubaVersion}{0.4.2}
|
||||
\newcommand{\MitsubaYear}{2012}
|
||||
|
||||
\typearea[current]{last}
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
\section{Python integration}
|
||||
\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
|
||||
used for many useful purposes. To access the API, start your Python
|
||||
interpreter and enter
|
||||
\begin{python}
|
||||
import mitsuba
|
||||
\end{python}
|
||||
\paragraph{Mac OS:}
|
||||
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.
|
||||
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
|
||||
specify the required extension search path before issuing the \code{import} command, e.g.:
|
||||
\begin{python}
|
||||
|
@ -29,6 +36,9 @@ os.environ['PATH'] = 'path-to-mitsuba-directory' + os.pathsep + os.environ['PATH
|
|||
|
||||
import mitsuba
|
||||
\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
|
||||
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}
|
||||
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
|
||||
this information.
|
||||
don't document what these functions actually do. The web API documentation is
|
||||
the preferred source of this information.
|
||||
|
||||
\subsection{Basics}
|
||||
Generally, the Python API tries to mimic the C++ API as closely as possible.
|
||||
|
|
|
@ -70,6 +70,10 @@ struct Normal : public TVector3<Float> {
|
|||
}
|
||||
};
|
||||
|
||||
inline Normal normalize(const Normal &n) {
|
||||
return n / n.length();
|
||||
}
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
||||
#endif /* __MITSUBA_CORE_NORMAL_H_ */
|
||||
|
|
|
@ -40,7 +40,7 @@ extern const int MTS_EXPORT_CORE primeTable[primeTableSize];
|
|||
/// Van der Corput radical inverse in base 2 with single precision
|
||||
inline float radicalInverse2Single(uint32_t n, uint32_t scramble = 0U) {
|
||||
/* 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);
|
||||
#else
|
||||
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
|
||||
inline double radicalInverse2Double(uint64_t n, uint64_t scramble = 0ULL) {
|
||||
/* 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);
|
||||
#else
|
||||
n = (n << 32) | (n >> 32);
|
||||
|
|
|
@ -417,8 +417,10 @@ public:
|
|||
*
|
||||
* Note that the resource's won't be removed until all processes using
|
||||
* 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
|
||||
|
|
|
@ -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_thread_num omp_get_thread_num
|
||||
#endif
|
||||
#else
|
||||
#define mts_omp_get_max_threads() 1
|
||||
#define mts_omp_get_thread_num() 0
|
||||
#endif
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -358,11 +358,14 @@ extern MTS_EXPORT_CORE bool solveQuadraticDouble(double a, double b,
|
|||
* Position of the last knot
|
||||
* \param size
|
||||
* Denotes the size of the \c data array
|
||||
* \param extrapolate
|
||||
* Extrapolate data values when \c x is out of range? (default: \c false)
|
||||
* \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,
|
||||
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
|
||||
|
@ -382,11 +385,14 @@ extern MTS_EXPORT_CORE Float interpCubic1D(Float x, const Float *data,
|
|||
* the entries of \c nodes.
|
||||
* \param size
|
||||
* Denotes the size of the \c data array
|
||||
* \param extrapolate
|
||||
* Extrapolate data values when \c x is out of range? (default: \c false)
|
||||
* \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,
|
||||
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
|
||||
|
@ -407,11 +413,14 @@ extern MTS_EXPORT Float interpCubic1DIrregular(Float x, const Float *nodes,
|
|||
* Position of the last knot on each dimension
|
||||
* \param size
|
||||
* 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
|
||||
* 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,
|
||||
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
|
||||
|
@ -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.
|
||||
* \param size
|
||||
* 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
|
||||
* 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,
|
||||
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
|
||||
|
@ -461,11 +473,14 @@ extern MTS_EXPORT_CORE Float interpCubic2DIrregular(const Point2 &p, const Float
|
|||
* Position of the last knot on each dimension
|
||||
* \param size
|
||||
* 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
|
||||
* 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,
|
||||
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
|
||||
|
@ -490,11 +505,14 @@ extern MTS_EXPORT_CORE Float interpCubic3D(const Point3 &p, const Float *data,
|
|||
* then 'y', and finally 'z' increments.
|
||||
* \param size
|
||||
* 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
|
||||
* 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,
|
||||
const Float *data, const Size3 &size);
|
||||
const Float *data, const Size3 &size, bool extrapolate = false);
|
||||
|
||||
//// Convert radians to degrees
|
||||
inline Float radToDeg(Float value) { return value * (180.0f / M_PI); }
|
||||
|
|
|
@ -26,7 +26,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \brief Current release of Mitsuba
|
||||
* \ingroup libcore
|
||||
*/
|
||||
#define MTS_VERSION "0.4.1"
|
||||
#define MTS_VERSION "0.4.2"
|
||||
|
||||
/**
|
||||
* \brief Year of the current release
|
||||
|
|
|
@ -206,8 +206,15 @@ public:
|
|||
*/
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -65,6 +65,10 @@ MTS_NAMESPACE_BEGIN
|
|||
* This parameter can be used to scale the the amount of illumination
|
||||
* 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}{
|
||||
* Specifies the relative amount of samples
|
||||
* allocated to this emitter. \default{1}
|
||||
|
@ -74,7 +78,6 @@ MTS_NAMESPACE_BEGIN
|
|||
* Preetham et al. \cite{Preetham1999Practical}. Using the provided position
|
||||
* and time information (see \pluginref{sky} for details), it can determine the
|
||||
* 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
|
||||
* 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).
|
||||
|
@ -146,6 +149,23 @@ public:
|
|||
Emitter *getElement(size_t i) {
|
||||
if (i != 0)
|
||||
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
|
||||
individual pixels for coverage (which is what Mitsuba 0.3.0 did)
|
||||
was slow and not very effective; for instance the power varied
|
||||
|
|
|
@ -74,16 +74,22 @@ MTS_NAMESPACE_BEGIN
|
|||
* This parameter can be used to separately scale the the amount of illumination
|
||||
* 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{
|
||||
* \medrendering{\pluginref{sky} emitter}{emitter_sunsky_sky}
|
||||
* \medrendering{\pluginref{sun} emitter}{emitter_sunsky_sun}
|
||||
* \medrendering{\pluginref{sunsky} emitter}{emitter_sunsky_sunsky}
|
||||
* \vspace{-2mm}
|
||||
* \caption{A coated rough copper test ball lit with the three
|
||||
* provided daylight illumination models}
|
||||
* }
|
||||
* \vspace{5mm}
|
||||
* \vspace{1mm}
|
||||
* This convenience plugin has the sole purpose of instantiating
|
||||
* \pluginref{sun} and \pluginref{sky} and merging them into a joint
|
||||
* environment map. Please refer to these plugins individually for more
|
||||
|
@ -95,7 +101,8 @@ public:
|
|||
: Emitter(props) {
|
||||
Float scale = props.getFloat("scale", 1.0f),
|
||||
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);
|
||||
|
||||
|
@ -159,39 +166,53 @@ public:
|
|||
props.getFloat("turbidity", 3.0f)) * sunScale;
|
||||
sun.elevation *= props.getFloat("stretch", 1.0f);
|
||||
Frame sunFrame = Frame(toSphere(sun));
|
||||
|
||||
Float theta = degToRad(SUN_APP_RADIUS * 0.5f);
|
||||
|
||||
size_t pixelCount = resolution*resolution/2;
|
||||
Float cosTheta = std::cos(theta * props.getFloat("sunRadiusScale", 1.0f));
|
||||
if (sunRadiusScale == 0) {
|
||||
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 */
|
||||
Float coveredPortion = 0.5f * (1 - cosTheta);
|
||||
m_dirEmitter = static_cast<Emitter *>(
|
||||
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,
|
||||
be very conservative */
|
||||
size_t nSamples = (size_t) std::max((Float) 100,
|
||||
(pixelCount * coveredPortion * 1000));
|
||||
/* Ratio of the sphere that is covered by the sun */
|
||||
Float coveredPortion = 0.5f * (1 - cosTheta);
|
||||
|
||||
factor = Point2(bitmap->getWidth() / (2*M_PI),
|
||||
bitmap->getHeight() / M_PI);
|
||||
/* Approx. number of samples that need to be generated,
|
||||
be very conservative */
|
||||
size_t nSamples = (size_t) std::max((Float) 100,
|
||||
(pixelCount * coveredPortion * 1000));
|
||||
|
||||
Spectrum value =
|
||||
sunRadiance * (2 * M_PI * (1-std::cos(theta))) *
|
||||
(bitmap->getWidth() * bitmap->getHeight())
|
||||
/ (2.0f * M_PI * M_PI * (Float) nSamples);
|
||||
factor = Point2(bitmap->getWidth() / (2*M_PI),
|
||||
bitmap->getHeight() / M_PI);
|
||||
|
||||
for (size_t i=0; i<nSamples; ++i) {
|
||||
Vector dir = sunFrame.toWorld(
|
||||
Warp::squareToUniformCone(cosTheta, sample02(i)));
|
||||
Spectrum value =
|
||||
sunRadiance * (2 * M_PI * (1-std::cos(theta))) *
|
||||
(bitmap->getWidth() * bitmap->getHeight())
|
||||
/ (2.0f * M_PI * M_PI * (Float) nSamples);
|
||||
|
||||
Float sinTheta = math::safe_sqrt(1-dir.y*dir.y);
|
||||
SphericalCoordinates sphCoords = fromSphere(dir);
|
||||
for (size_t i=0; i<nSamples; ++i) {
|
||||
Vector dir = sunFrame.toWorld(
|
||||
Warp::squareToUniformCone(cosTheta, sample02(i)));
|
||||
|
||||
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));
|
||||
Float sinTheta = math::safe_sqrt(1-dir.y*dir.y);
|
||||
SphericalCoordinates sphCoords = fromSphere(dir);
|
||||
|
||||
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());
|
||||
|
@ -204,7 +225,7 @@ public:
|
|||
envProps.setData("bitmap", bitmapData);
|
||||
envProps.setTransform("toWorld", trafo);
|
||||
envProps.setFloat("samplingWeight", m_samplingWeight);
|
||||
m_emitter = static_cast<Emitter *>(
|
||||
m_envEmitter = static_cast<Emitter *>(
|
||||
PluginManager::getInstance()->createObject(
|
||||
MTS_CLASS(Emitter), envProps));
|
||||
|
||||
|
@ -217,17 +238,24 @@ public:
|
|||
|
||||
SunSkyEmitter(Stream *stream, InstanceManager *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 {
|
||||
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() {
|
||||
Emitter::configure();
|
||||
m_emitter->configure();
|
||||
m_envEmitter->configure();
|
||||
if (m_dirEmitter)
|
||||
m_dirEmitter->configure();
|
||||
}
|
||||
|
||||
bool isCompound() const {
|
||||
|
@ -240,14 +268,17 @@ public:
|
|||
|
||||
Emitter *getElement(size_t i) {
|
||||
if (i == 0)
|
||||
return m_emitter;
|
||||
return m_envEmitter;
|
||||
else if (i == 1)
|
||||
return m_dirEmitter;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
ref<Emitter> m_emitter;
|
||||
ref<Emitter> m_dirEmitter;
|
||||
ref<Emitter> m_envEmitter;
|
||||
};
|
||||
|
||||
MTS_IMPLEMENT_CLASS_S(SunSkyEmitter, false, Emitter)
|
||||
|
|
|
@ -643,7 +643,11 @@ bool SpecularManifold::update(Path &path, int start, int end) {
|
|||
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
|
||||
&v = m_vertices[j],
|
||||
&vn = m_vertices[j+1];
|
||||
|
@ -664,7 +668,8 @@ bool SpecularManifold::update(Path &path, int start, int end) {
|
|||
PathVertex::EMediumInteraction : PathVertex::ESurfaceInteraction;
|
||||
|
||||
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;
|
||||
temp.type = PathVertex::EMediumInteraction;
|
||||
temp.degenerate = false;
|
||||
|
@ -681,7 +686,7 @@ bool SpecularManifold::update(Path &path, int start, int end) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (m_vertices.size() > 3) {
|
||||
if (m_vertices.size() >= 3) {
|
||||
PathVertex *succ2 = path.vertex(i+2*step);
|
||||
PathEdge *succ2Edge = path.edge(predEdgeIdx + 2*step);
|
||||
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) {
|
||||
if (a == 0) {
|
||||
if (a == 0)
|
||||
++a;
|
||||
if (!path.vertex(a)->isConnectable())
|
||||
++a;
|
||||
} else if (a == path.length()) {
|
||||
else if (a == path.length())
|
||||
--a;
|
||||
if (!path.vertex(a)->isConnectable())
|
||||
--a;
|
||||
}
|
||||
if (b == 0) {
|
||||
if (b == 0)
|
||||
++b;
|
||||
if (!path.vertex(b)->isConnectable())
|
||||
++b;
|
||||
} else if (b == path.length()) {
|
||||
else if (b == path.length())
|
||||
--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;
|
||||
|
||||
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()) {
|
||||
result *= G(path, start, i);
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
BDAssert(start == b);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -272,8 +272,10 @@ bool ManifoldPerturbation::sampleMutation(
|
|||
for (int i=l+1; i<m; ++i) {
|
||||
proposal.append(m_pool.allocVertex());
|
||||
proposal.append(m_pool.allocEdge());
|
||||
memset(proposal.vertex(proposal.vertexCount()-1), 0, sizeof(PathVertex)); /// XXX
|
||||
}
|
||||
proposal.append(source, m, k+1);
|
||||
|
||||
proposal.vertex(a) = proposal.vertex(a)->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) ||
|
||||
(vb_old->isMediumInteraction() && m_thetaDiffMediumSamples < DIFF_SAMPLES)) {
|
||||
if (((vb_old->isSurfaceInteraction() && m_thetaDiffSurfaceSamples < DIFF_SAMPLES) ||
|
||||
(vb_old->isMediumInteraction() && m_thetaDiffMediumSamples < DIFF_SAMPLES)) && b+1 != k && b-1 != 0) {
|
||||
LockGuard guard(m_thetaDiffMutex);
|
||||
|
||||
if ((vb_old->isSurfaceInteraction() && m_thetaDiffSurfaceSamples < DIFF_SAMPLES) ||
|
||||
|
@ -586,6 +588,7 @@ bool ManifoldPerturbation::sampleMutation(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PathVertex::connect(m_scene,
|
||||
proposal.vertexOrNull(q-1),
|
||||
proposal.edgeOrNull(q-1),
|
||||
|
|
|
@ -580,7 +580,9 @@ Float PathSampler::computeAverageLuminance(size_t sampleCount) {
|
|||
}
|
||||
|
||||
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) {
|
||||
const Float *luminanceValues = importanceMap->getFloatData();
|
||||
Vector2i size = importanceMap->getSize();
|
||||
|
@ -608,40 +610,40 @@ Float PathSampler::generateSeeds(size_t sampleCount, size_t seedCount,
|
|||
tempSeeds.reserve(sampleCount);
|
||||
|
||||
SplatList splatList;
|
||||
Float luminance;
|
||||
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;
|
||||
for (size_t i=0; i<sampleCount; ++i) {
|
||||
size_t seedIndex = tempSeeds.size();
|
||||
size_t sampleIndex = m_sensorSampler->getSampleIndex();
|
||||
Float lum = 0.0f;
|
||||
luminance = 0.0f;
|
||||
|
||||
if (fineGrained) {
|
||||
samplePaths(Point2i(-1), callback);
|
||||
|
||||
/* Fine seed granularity (e.g. for Veach-MLT).
|
||||
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;
|
||||
lum += tempSeeds[j].luminance;
|
||||
}
|
||||
} else {
|
||||
/* Run the path sampling strategy */
|
||||
sampleSplats(Point2i(-1), splatList);
|
||||
luminance = splatList.luminance;
|
||||
splatList.normalize(importanceMap);
|
||||
lum = splatList.luminance;
|
||||
|
||||
/* Coarse seed granularity (e.g. for PSSMLT) */
|
||||
if (lum != 0)
|
||||
tempSeeds.push_back(PathSeed(sampleIndex, lum));
|
||||
if (luminance != 0)
|
||||
tempSeeds.push_back(PathSeed(sampleIndex, luminance));
|
||||
}
|
||||
|
||||
/* Numerically robust online variance estimation using an
|
||||
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);
|
||||
variance += delta * (lum - mean);
|
||||
variance += delta * (luminance - mean);
|
||||
}
|
||||
BDAssert(m_pool.unused());
|
||||
Float stddev = std::sqrt(variance / (sampleCount-1));
|
||||
|
|
|
@ -158,10 +158,11 @@ void Scheduler::retainResource(int id) {
|
|||
rec->refCount++;
|
||||
}
|
||||
|
||||
void Scheduler::unregisterResource(int id) {
|
||||
bool Scheduler::unregisterResource(int id) {
|
||||
LockGuard lock(m_mutex);
|
||||
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];
|
||||
if (--rec->refCount == 0) {
|
||||
|
@ -175,6 +176,7 @@ void Scheduler::unregisterResource(int id) {
|
|||
for (size_t i=0; i<m_workers.size(); ++i)
|
||||
m_workers[i]->signalResourceExpiration(id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SerializableObject *Scheduler::getResource(int id, int coreIndex) {
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
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
|
||||
#include <mitsuba/mitsuba.h>
|
||||
#include <mitsuba/core/ssemath.h>
|
||||
|
|
|
@ -134,7 +134,7 @@ void Statistics::logPlugin(const std::string &name, const std::string &descr) {
|
|||
}
|
||||
|
||||
void Statistics::printStats() {
|
||||
SLog(EInfo, "Statistics: \n%s", getStats().c_str());
|
||||
SLog(EInfo, "Statistics:\n%s", getStats().c_str());
|
||||
}
|
||||
|
||||
std::string Statistics::getStats() {
|
||||
|
|
|
@ -469,9 +469,9 @@ bool solveLinearSystem2x2(const Float a[2][2], const Float b[2], Float x[2]) {
|
|||
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 */
|
||||
if (!(x >= min && x <= max))
|
||||
if (!(x >= min && x <= max) && !extrapolate)
|
||||
return 0.0f;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 */
|
||||
if (!(x >= nodes[0] && x <= nodes[size-1]))
|
||||
if (!(x >= nodes[0] && x <= nodes[size-1]) && !extrapolate)
|
||||
return 0.0f;
|
||||
|
||||
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,
|
||||
const Point2 &min, const Point2 &max, const Size2 &size) {
|
||||
const Point2 &min, const Point2 &max, const Size2 &size, bool extrapolate) {
|
||||
Float knotWeights[2][4];
|
||||
Size2 knot;
|
||||
|
||||
|
@ -553,7 +553,7 @@ Float interpCubic2D(const Point2 &p, const Float *data,
|
|||
for (int dim=0; dim<2; ++dim) {
|
||||
Float *weights = knotWeights[dim];
|
||||
/* 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;
|
||||
|
||||
/* 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_,
|
||||
const Float *data, const Size2 &size) {
|
||||
const Float *data, const Size2 &size, bool extrapolate) {
|
||||
Float knotWeights[2][4];
|
||||
Size2 knot;
|
||||
|
||||
|
@ -625,7 +625,7 @@ Float interpCubic2DIrregular(const Point2 &p, const Float **nodes_,
|
|||
Float *weights = knotWeights[dim];
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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,
|
||||
const Point3 &min, const Point3 &max, const Size3 &size) {
|
||||
const Point3 &min, const Point3 &max, const Size3 &size, bool extrapolate) {
|
||||
Float knotWeights[3][4];
|
||||
Size3 knot;
|
||||
|
||||
|
@ -697,7 +697,7 @@ Float interpCubic3D(const Point3 &p, const Float *data,
|
|||
for (int dim=0; dim<3; ++dim) {
|
||||
Float *weights = knotWeights[dim];
|
||||
/* 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;
|
||||
|
||||
/* 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_,
|
||||
const Float *data, const Size3 &size) {
|
||||
const Float *data, const Size3 &size, bool extrapolate) {
|
||||
Float knotWeights[3][4];
|
||||
Size3 knot;
|
||||
|
||||
|
@ -773,7 +773,7 @@ Float interpCubic3DIrregular(const Point3 &p, const Float **nodes_,
|
|||
Float *weights = knotWeights[dim];
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Find the index of the left knot in the queried subinterval, be
|
||||
|
|
|
@ -542,7 +542,7 @@ void TriMesh::rebuildTopology(Float maxAngle) {
|
|||
configure();
|
||||
}
|
||||
|
||||
void TriMesh::computeNormals() {
|
||||
void TriMesh::computeNormals(bool force) {
|
||||
int invalidNormals = 0;
|
||||
if (m_faceNormals) {
|
||||
if (m_normals) {
|
||||
|
@ -558,7 +558,7 @@ void TriMesh::computeNormals() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (m_normals) {
|
||||
if (m_normals && !force) {
|
||||
if (m_flipNormals) {
|
||||
for (size_t i=0; i<m_vertexCount; i++)
|
||||
m_normals[i] *= -1;
|
||||
|
@ -566,7 +566,8 @@ void TriMesh::computeNormals() {
|
|||
/* Do nothing */
|
||||
}
|
||||
} else {
|
||||
m_normals = new Normal[m_vertexCount];
|
||||
if (!m_normals)
|
||||
m_normals = new Normal[m_vertexCount];
|
||||
memset(m_normals, 0, sizeof(Normal)*m_vertexCount);
|
||||
|
||||
/* Well-behaved vertex normal computation based on
|
||||
|
@ -617,7 +618,7 @@ void TriMesh::computeNormals() {
|
|||
}
|
||||
|
||||
void TriMesh::computeUVTangents() {
|
||||
int degenerate = 0;
|
||||
// int degenerate = 0;
|
||||
if (!m_texcoords) {
|
||||
bool anisotropic = hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropic;
|
||||
if (anisotropic)
|
||||
|
@ -654,7 +655,7 @@ void TriMesh::computeUVTangents() {
|
|||
Normal n = Normal(cross(dP1, dP2));
|
||||
Float length = n.length();
|
||||
if (length == 0) {
|
||||
++degenerate;
|
||||
// ++degenerate;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -670,9 +671,12 @@ void TriMesh::computeUVTangents() {
|
|||
}
|
||||
}
|
||||
|
||||
if (degenerate > 0)
|
||||
Log(EWarn, "\"%s\": computeTangentSpace(): Mesh contains %i "
|
||||
"degenerate triangles!", getName().c_str(), degenerate);
|
||||
#if 0
|
||||
/* Don't be so noisy -- this isn't usually a problem.. */
|
||||
if (degenerate > 0)
|
||||
Log(EWarn, "\"%s\": computeTangentSpace(): Mesh contains %i "
|
||||
"degenerate triangles!", getName().c_str(), degenerate);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TriMesh::getNormalDerivative(const Intersection &its,
|
||||
|
|
|
@ -1593,7 +1593,7 @@ void MainWindow::on_actionStartServer_triggered() {
|
|||
|
||||
void MainWindow::on_actionEnableCommandLine_triggered() {
|
||||
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 "
|
||||
"repeat this process every time the Mitsuba application is moved.</p>"
|
||||
"<p>Create links?</p>"),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <AuthorizationTags.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace mitsuba {
|
||||
extern std::string __mts_bundlepath();
|
||||
|
@ -30,7 +31,10 @@ bool create_symlinks() {
|
|||
}
|
||||
std::string bundlePath = mitsuba::__mts_bundlepath();
|
||||
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;
|
||||
flags = kAuthorizationFlagDefaults;
|
||||
status = AuthorizationExecuteWithPrivileges(ref, const_cast<char *>(path.c_str()), flags, args, &pipe);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <pwd.h>
|
||||
|
||||
void installPython(const char *basedir, const char *version) {
|
||||
char fname[MAXPATHLEN];
|
||||
|
@ -23,6 +24,22 @@ void installPython(const char *basedir, const char *version) {
|
|||
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) {
|
||||
char fname[MAXPATHLEN];
|
||||
FILE *f;
|
||||
|
@ -51,7 +68,7 @@ void install(const char *basedir, const char *name) {
|
|||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Incorrect number of arguments!\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -68,6 +85,11 @@ int main(int argc, char **argv) {
|
|||
install(argv[1], "mtsimport");
|
||||
installPython(argv[1], "2.6");
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -510,8 +510,8 @@ public:
|
|||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "Cylinder[" << endl
|
||||
<< " radius = " << m_radius << ", " << endl
|
||||
<< " length = " << m_length << ", " << endl
|
||||
<< " radius = " << m_radius << "," << endl
|
||||
<< " length = " << m_length << "," << endl
|
||||
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl
|
||||
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
|
||||
if (isMediumTransition())
|
||||
|
|
|
@ -261,7 +261,7 @@ public:
|
|||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "Disk[" << endl
|
||||
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << ", " << endl
|
||||
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl
|
||||
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
|
||||
if (isMediumTransition()) {
|
||||
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl
|
||||
|
|
|
@ -39,7 +39,6 @@ MTS_NAMESPACE_BEGIN
|
|||
Instance::Instance(const Properties &props) : Shape(props) {
|
||||
m_objectToWorld = props.getTransform("toWorld", Transform());
|
||||
m_worldToObject = m_objectToWorld.inverse();
|
||||
m_invScale = 1.0f/m_objectToWorld(Vector(0, 0, 1)).length();
|
||||
}
|
||||
|
||||
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_objectToWorld = Transform(stream);
|
||||
m_worldToObject = m_objectToWorld.inverse();
|
||||
m_invScale = stream->readFloat();
|
||||
}
|
||||
|
||||
void Instance::serialize(Stream *stream, InstanceManager *manager) const {
|
||||
Shape::serialize(stream, manager);
|
||||
manager->serialize(stream, m_shapeGroup.get());
|
||||
m_objectToWorld.serialize(stream);
|
||||
stream->writeFloat(m_invScale);
|
||||
}
|
||||
|
||||
void Instance::configure() {
|
||||
|
@ -130,17 +127,26 @@ void Instance::fillIntersectionRecord(const Ray &_ray,
|
|||
|
||||
void Instance::getNormalDerivative(const Intersection &its,
|
||||
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);
|
||||
temp.p = m_worldToObject(its.p);
|
||||
temp.dpdu = m_worldToObject(its.dpdu);
|
||||
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);
|
||||
|
||||
/* The following will probably be incorrect for
|
||||
non-rigid transformations */
|
||||
dndu = m_objectToWorld(Normal(dndu))*m_invScale;
|
||||
dndv = m_objectToWorld(Normal(dndv))*m_invScale;
|
||||
dndu = m_objectToWorld(Normal(dndu)) * invLen;
|
||||
dndv = m_objectToWorld(Normal(dndv)) * invLen;
|
||||
|
||||
dndu -= tn * dot(tn, dndu);
|
||||
dndv -= tn * dot(tn, dndv);
|
||||
}
|
||||
|
||||
MTS_IMPLEMENT_CLASS_S(Instance, false, Shape)
|
||||
|
|
|
@ -81,7 +81,6 @@ public:
|
|||
private:
|
||||
ref<ShapeGroup> m_shapeGroup;
|
||||
Transform m_objectToWorld, m_worldToObject;
|
||||
Float m_invScale;
|
||||
};
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -229,7 +229,7 @@ public:
|
|||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "Rectangle[" << endl
|
||||
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << ", " << endl;
|
||||
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl;
|
||||
if (isMediumTransition())
|
||||
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl
|
||||
<< " exteriorMedium = " << indent(m_exteriorMedium.toString()) << "," << endl;
|
||||
|
|
|
@ -170,7 +170,7 @@ size_t ShapeGroup::getEffectivePrimitiveCount() const {
|
|||
std::string ShapeGroup::toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "ShapeGroup[" << endl
|
||||
<< " name = \"" << m_name << "\", " << endl
|
||||
<< " name = \"" << m_name << "\"," << endl
|
||||
<< " primCount = " << m_kdtree->getPrimitiveCount() << endl
|
||||
<< "]";
|
||||
return oss.str();
|
||||
|
|
|
@ -468,8 +468,8 @@ public:
|
|||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "Sphere[" << endl
|
||||
<< " radius = " << m_radius << ", " << endl
|
||||
<< " center = " << m_center.toString() << ", " << endl
|
||||
<< " radius = " << m_radius << "," << endl
|
||||
<< " center = " << m_center.toString() << "," << endl
|
||||
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
|
||||
if (isMediumTransition())
|
||||
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl
|
||||
|
|
Loading…
Reference in New Issue