Merge with upstream

metadata
Edgar Velazquez-Armendariz 2013-01-28 20:12:13 -05:00
commit 08e6f4cd6a
81 changed files with 2157 additions and 675 deletions

View File

@ -6,7 +6,7 @@ CCFLAGS = ['-arch', 'x86_64', '-mmacosx-version-min=10.7', '-march=nocona
LINKFLAGS = ['-framework', 'OpenGL', '-framework', 'Cocoa', '-arch', 'x86_64', '-mmacosx-version-min=10.7', '-Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk', '-Wl,-headerpad,128'] LINKFLAGS = ['-framework', 'OpenGL', '-framework', 'Cocoa', '-arch', 'x86_64', '-mmacosx-version-min=10.7', '-Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk', '-Wl,-headerpad,128']
BASEINCLUDE = ['#include', '#dependencies/include'] BASEINCLUDE = ['#include', '#dependencies/include']
BASELIBDIR = ['#dependencies/lib'] BASELIBDIR = ['#dependencies/lib']
BASELIB = ['m', 'pthread', 'gomp', 'Half'] BASELIB = ['m', 'pthread', 'Half']
OEXRINCLUDE = ['#dependencies/include/OpenEXR'] OEXRINCLUDE = ['#dependencies/include/OpenEXR']
OEXRLIB = ['IlmImf', 'Imath', 'Iex', 'z'] OEXRLIB = ['IlmImf', 'Imath', 'Iex', 'z']
PNGLIB = ['png'] PNGLIB = ['png']

View File

@ -35,13 +35,14 @@ mkdir -p $RPM_BUILD_ROOT/usr/share/mitsuba/plugins
mkdir -p $RPM_BUILD_ROOT/usr/share/pixmaps mkdir -p $RPM_BUILD_ROOT/usr/share/pixmaps
mkdir -p $RPM_BUILD_ROOT/usr/share/applications mkdir -p $RPM_BUILD_ROOT/usr/share/applications
mkdir -p $RPM_BUILD_ROOT/usr/include mkdir -p $RPM_BUILD_ROOT/usr/include
strip dist/lib* dist/mtsgui dist/mitsuba dist/mtssrv dist/mtsutil strip dist/lib* dist/mtsgui dist/mitsuba dist/mtssrv dist/mtsutil dist/mtsimport
strip dist/plugins/* dist/python/*/* strip dist/plugins/* dist/python/*/*
cp dist/libmitsuba-*.so $RPM_BUILD_ROOT%{_libdir} cp dist/libmitsuba-*.so $RPM_BUILD_ROOT%{_libdir}
cp dist/mtsgui $RPM_BUILD_ROOT%{_bindir} cp dist/mtsgui $RPM_BUILD_ROOT%{_bindir}
cp dist/mitsuba $RPM_BUILD_ROOT%{_bindir} cp dist/mitsuba $RPM_BUILD_ROOT%{_bindir}
cp dist/mtssrv $RPM_BUILD_ROOT%{_bindir} cp dist/mtssrv $RPM_BUILD_ROOT%{_bindir}
cp dist/mtsutil $RPM_BUILD_ROOT%{_bindir} cp dist/mtsutil $RPM_BUILD_ROOT%{_bindir}
cp dist/mtsimport $RPM_BUILD_ROOT%{_bindir}
cp dist/python/2.7/mitsuba.so $RPM_BUILD_ROOT%{_libdir}/python2.7/lib-dynload cp dist/python/2.7/mitsuba.so $RPM_BUILD_ROOT%{_libdir}/python2.7/lib-dynload
cp dist/plugins/* $RPM_BUILD_ROOT/usr/share/mitsuba/plugins cp dist/plugins/* $RPM_BUILD_ROOT/usr/share/mitsuba/plugins
cp -Rdp dist/data $RPM_BUILD_ROOT/usr/share/mitsuba/data cp -Rdp dist/data $RPM_BUILD_ROOT/usr/share/mitsuba/data

View File

@ -26,7 +26,7 @@
<xsd:element name="rgb" type="rgb"/> <xsd:element name="rgb" type="rgb"/>
<xsd:element name="srgb" type="string"/> <xsd:element name="srgb" type="string"/>
<xsd:element name="blackbody" type="blackbody"/> <xsd:element name="blackbody" type="blackbody"/>
<xsd:element name="alias" type="alias"/> <xsd:element name="alias" type="alias"/>
</xsd:choice> </xsd:choice>
<xsd:attribute name="version" type="xsd:string"/> <xsd:attribute name="version" type="xsd:string"/>
@ -43,6 +43,7 @@
<xsd:element name="vector" type="point"/> <xsd:element name="vector" type="point"/>
<xsd:element name="boolean" type="boolean"/> <xsd:element name="boolean" type="boolean"/>
<xsd:element name="transform" type="transform"/> <xsd:element name="transform" type="transform"/>
<xsd:element name="animation" type="animation"/>
<xsd:element name="string" type="string"/> <xsd:element name="string" type="string"/>
<xsd:element name="spectrum" type="spectrum"/> <xsd:element name="spectrum" type="spectrum"/>
<xsd:element name="rgb" type="rgb"/> <xsd:element name="rgb" type="rgb"/>
@ -50,14 +51,14 @@
<xsd:element name="blackbody" type="blackbody"/> <xsd:element name="blackbody" type="blackbody"/>
</xsd:choice> </xsd:choice>
</xsd:group> </xsd:group>
<xsd:complexType name="objectBase"> <xsd:complexType name="objectBase">
<xsd:attribute name="type" type="xsd:string" use="required"/> <xsd:attribute name="type" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string"/> <xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="id" type="xsd:string"/> <xsd:attribute name="id" type="xsd:string"/>
</xsd:complexType> </xsd:complexType>
<xsd:complexType name="object"> <xsd:complexType name="object">
<xsd:complexContent> <xsd:complexContent>
<xsd:extension base="objectBase"> <xsd:extension base="objectBase">
@ -140,7 +141,7 @@
</xsd:extension> </xsd:extension>
</xsd:complexContent> </xsd:complexContent>
</xsd:complexType> </xsd:complexType>
<!-- MEDIUM Element --> <!-- MEDIUM Element -->
<xsd:complexType name="medium"> <xsd:complexType name="medium">
<xsd:complexContent> <xsd:complexContent>
@ -297,7 +298,7 @@
<xsd:complexType name="include"> <xsd:complexType name="include">
<xsd:attribute name="filename" type="xsd:string" use="required"/> <xsd:attribute name="filename" type="xsd:string" use="required"/>
</xsd:complexType> </xsd:complexType>
<xsd:complexType name="alias"> <xsd:complexType name="alias">
<xsd:attribute name="id" type="xsd:string" use="required"/> <xsd:attribute name="id" type="xsd:string" use="required"/>
<xsd:attribute name="as" type="xsd:string" use="required"/> <xsd:attribute name="as" type="xsd:string" use="required"/>
@ -314,6 +315,23 @@
</xsd:choice> </xsd:choice>
<xsd:attribute name="name" type="xsd:string" use="required"/> <xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType> </xsd:complexType>
<xsd:complexType name="animationTransform">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="translate" type="translate"/>
<xsd:element name="rotate" type="rotate"/>
<xsd:element name="lookAt" type="lookAt"/>
<xsd:element name="lookat" type="lookAt"/>
<xsd:element name="scale" type="scale"/>
<xsd:element name="matrix" type="matrix"/>
</xsd:choice>
<xsd:attribute name="time" type="doubleType" use="required"/>
</xsd:complexType>
<xsd:complexType name="animation">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="transform" type="animationTransform"/>
</xsd:choice>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="translate"> <xsd:complexType name="translate">
<xsd:attribute name="x" type="doubleType"/> <xsd:attribute name="x" type="doubleType"/>
<xsd:attribute name="y" type="doubleType"/> <xsd:attribute name="y" type="doubleType"/>

View File

@ -276,7 +276,7 @@ choices are available:
\begin{xml} \begin{xml}
<matrix value="0 -0.53 0 -1.79 0.92 0 0 8.03 0 0 0.53 0 0 0 0 1"/> <matrix value="0 -0.53 0 -1.79 0.92 0 0 8.03 0 0 0.53 0 0 0 0 1"/>
\end{xml} \end{xml}
\item lookat transformations --- this is primarily useful for setting up cameras (and spot lights). The \code{origin} coordinates \item \code{lookat} transformations --- this is primarily useful for setting up cameras (and spot lights). The \code{origin} coordinates
specify the camera origin, \code{target} is the point that the camera will look at, and the specify the camera origin, \code{target} is the point that the camera will look at, and the
(optional) \code{up} parameter determines the ``upward'' direction in the final rendered image. (optional) \code{up} parameter determines the ``upward'' direction in the final rendered image.
The \code{up} parameter is not needed for spot lights. The \code{up} parameter is not needed for spot lights.
@ -286,7 +286,33 @@ The \code{up} parameter is not needed for spot lights.
\end{itemize} \end{itemize}
Cordinates that are zero (for \code{translate} and \code{rotate}) or one (for \code{scale}) Cordinates that are zero (for \code{translate} and \code{rotate}) or one (for \code{scale})
do not explicitly have to be specified. do not explicitly have to be specified.
\subsection{Instancing} \subsection{Animated transformations}
Most shapes, emitters, and sensors in Mitsuba can accept both normal transformations
and \emph{animated transformations} as parameters. The latter is useful to
render scenes involving motion blur. The syntax used to specify these
is slightly different:
\begin{xml}
<animation name="trafoProperty">
<transform time="0">
.. chained list of transformations as discussed above ..
</transform>
<transform time="1">
.. chained list of transformations as discussed above ..
</transform>
.. additional transformations (optional) ..
</animation>
\end{xml}
Mitsuba then decomposes each transformation into a scale, translation, and
rotation component and interpolates\footnote{Using linear interpolation
for the scale and translation component and spherical linear quaternion
interpolation for the rotation component.} these for intermediate
time values.
It is important to specify appropriate shutter open/close times
to the sensor so that the motion is visible.
\subsection{References}
Quite often, you will find yourself using an object (such as a material) in many places. To avoid having Quite often, you will find yourself using an object (such as a material) in many places. To avoid having
to declare it over and over again, which wastes memory, you can make use of references. Here is an example to declare it over and over again, which wastes memory, you can make use of references. Here is an example
of how this works: of how this works:

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@ -116,7 +116,7 @@
medium,film,sampler,integrator,emitter,sensor, medium,film,sampler,integrator,emitter,sensor,
translate,rotate,scale,lookat,point,vector,matrix, translate,rotate,scale,lookat,point,vector,matrix,
include,fscat,volume,alias,rfilter,boolean, include,fscat,volume,alias,rfilter,boolean,
subsurface subsurface,animation
}, },
} }

View File

@ -24,7 +24,7 @@ It is important to keep in mind that other applications may not support this
``linearized sRGB'' space---in particular, the Mac OS preview currently ``linearized sRGB'' space---in particular, the Mac OS preview currently
does not display images with this encoding correctly. does not display images with this encoding correctly.
\subsubsection{Spectral mode} \subsubsection{Spectral rendering}
Some predictive rendering applications will require a more realistic space for Some predictive rendering applications will require a more realistic space for
interreflection computations. In such cases, Mitsuba can be switched to \emph{spectral mode}. interreflection computations. In such cases, Mitsuba can be switched to \emph{spectral mode}.
This can be done by compiling it with the \code{SPECTRUM\_SAMPLES=}$n$ parameter This can be done by compiling it with the \code{SPECTRUM\_SAMPLES=}$n$ parameter

142
include/mitsuba/core/math.h Normal file
View File

@ -0,0 +1,142 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__MITSUBA_CORE_MATH_H_)
#define __MITSUBA_CORE_MATH_H_
MTS_NAMESPACE_BEGIN
namespace math {
#if defined(__LINUX__) && defined(__x86_64__)
/*
The Linux/x86_64 single precision implementations of 'exp'
and 'log' suffer from a serious performance regression.
It is about 5x faster to use the double-precision versions
with the extra overhead of the involved FP conversion.
Until this is fixed, the following aliases make sure that
the fastest implementation is used in every case.
*/
inline float fastexp(float value) {
return (float) ::exp((double) value);
}
inline double fastexp(double value) {
return ::exp(value);
}
inline float fastlog(float value) {
return (float) ::log((double) value);
}
inline double fastlog(double value) {
return ::log(value);
}
#else
inline float fastexp(float value) {
return ::expf(value);
}
inline double fastexp(double value) {
return ::exp(value);
}
inline float fastlog(float value) {
return ::logf(value);
}
inline double fastlog(double value) {
return ::log(value);
}
#endif
#if defined(_GNU_SOURCE)
inline void sincos(float theta, float *sin, float *cos) {
::sincosf(theta, sin, cos);
}
inline void sincos(double theta, double *sin, double *cos) {
::sincos(theta, sin, cos);
}
#else
inline void sincos(float theta, float *_sin, float *_cos) {
*_sin = sinf(theta);
*_cos = cosf(theta);
}
inline void sincos(double theta, double *_sin, double *_cos) {
*_sin = sin(theta);
*_cos = cos(theta);
}
#endif
/// Arcsine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline float safe_asin(float value) {
return std::asin(std::min(1.0f, std::max(-1.0f, value)));
}
/// Arcsine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline double safe_asin(double value) {
return std::asin(std::min(1.0, std::max(-1.0, value)));
}
/// Arccosine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline float safe_acos(float value) {
return std::acos(std::min(1.0f, std::max(-1.0f, value)));
}
/// Arccosine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline double safe_acos(double value) {
return std::acos(std::min(1.0, std::max(-1.0, value)));
}
/// Square root variant that gracefully handles arguments < 0 that are due to roundoff errors
inline float safe_sqrt(float value) {
return std::sqrt(std::max(0.0f, value));
}
/// Square root variant that gracefully handles arguments < 0 that are due to roundoff errors
inline double safe_sqrt(double value) {
return std::sqrt(std::max(0.0, value));
}
/// Simple signum function -- note that it returns the FP sign of the input (and never zero)
inline Float signum(Float value) {
#if defined(__WINDOWS__)
return (Float) _copysign(1.0, value);
#elif defined(SINGLE_PRECISION)
return copysignf((float) 1.0, value);
#elif defined(DOUBLE_PRECISION)
return copysign((double) 1.0, value);
#endif
}
}; /* namespace math */
MTS_NAMESPACE_END
#if defined(_MSC_VER)
extern "C" {
extern MTS_EXPORT_CORE float nextafterf(float x, float y);
extern MTS_EXPORT_CORE double nextafter(double x, double y);
};
#endif
#endif /* __MITSUBA_CORE_MATH_H_ */

View File

@ -59,6 +59,8 @@ public:
EVector, EVector,
/// 4x4 transform for homogeneous coordinates /// 4x4 transform for homogeneous coordinates
ETransform, ETransform,
/// An animated 4x4 transformation
EAnimatedTransform,
/// Discretized color spectrum /// Discretized color spectrum
ESpectrum, ESpectrum,
/// Arbitrary-length string /// Arbitrary-length string
@ -152,6 +154,15 @@ public:
/// Get a linear transformation (with default) /// Get a linear transformation (with default)
Transform getTransform(const std::string &name, const Transform &defVal) const; Transform getTransform(const std::string &name, const Transform &defVal) const;
/// Set an animated linear transformation
void setAnimatedTransform(const std::string &name, const AnimatedTransform *value, bool warnDuplicates = true);
/// Get an animated linear transformation
ref<const AnimatedTransform> getAnimatedTransform(const std::string &name) const;
/// Get an animated linear transformation (with default)
ref<const AnimatedTransform> getAnimatedTransform(const std::string &name, const AnimatedTransform *defVal) const;
/// Get an animated linear transformation (with default)
ref<const AnimatedTransform> getAnimatedTransform(const std::string &name, const Transform &defVal) const;
/// Set a spectral power distribution /// Set a spectral power distribution
void setSpectrum(const std::string &name, const Spectrum &value, bool warnDuplicates = true); void setSpectrum(const std::string &name, const Spectrum &value, bool warnDuplicates = true);
/// Get a spectral power distribution /// Get a spectral power distribution
@ -185,6 +196,10 @@ public:
/// Return one of the parameters (converting it to a string if necessary, with default value) /// Return one of the parameters (converting it to a string if necessary, with default value)
std::string getAsString(const std::string &name, const std::string &defVal) const; std::string getAsString(const std::string &name, const std::string &defVal) const;
/// Copy an attribute from another Properties object and potentially rename it
void copyAttribute(const Properties &properties,
const std::string &sourceName, const std::string &targetName);
/// Store an array containing the names of all stored properties /// Store an array containing the names of all stored properties
void putPropertyNames(std::vector<std::string> &results) const; void putPropertyNames(std::vector<std::string> &results) const;

View File

@ -132,12 +132,17 @@ template <typename T> struct TQuaternion {
/// Equality test /// Equality test
bool operator==(const TQuaternion &q) const { bool operator==(const TQuaternion &q) const {
return v == q.v && v.w == q.w; return v == q.v && w == q.w;
} }
/// Inequality test /// Inequality test
bool operator!=(const TQuaternion &q) const { bool operator!=(const TQuaternion &q) const {
return v != q.v || v.w != q.w; return v != q.v || w != q.w;
}
/// Identity test
bool isIdentity() const {
return v.isZero() && w == 1;
} }
/// Return the rotation axis of this quaternion /// Return the rotation axis of this quaternion
@ -216,39 +221,42 @@ template <typename T> struct TQuaternion {
} }
} }
inline static TQuaternion fromTransform(const Transform &trafo) {
return fromMatrix(trafo.getMatrix());
}
/** /**
* \brief Construct an unit quaternion matching the supplied * \brief Construct an unit quaternion matching the supplied
* rotation matrix. * rotation matrix.
*/ */
static TQuaternion fromTransform(const Transform trafo) { static TQuaternion fromMatrix(const Matrix4x4 &m) {
/// Implementation from PBRT /// Implementation from PBRT
const Matrix4x4 &m = trafo.getMatrix(); T trace = m(0, 0) + m(1, 1) + m(2, 2);
T trace = m.m[0][0] + m.m[1][1] + m.m[2][2];
TVector3<T> v; T w; TVector3<T> v; T w;
if (trace > 0.f) { if (trace > 0.f) {
// Compute w from matrix trace, then xyz // Compute w from matrix trace, then xyz
// 4w^2 = m[0][0] + m[1][1] + m[2][2] + m[3][3] (but m[3][3] == 1) // 4w^2 = m[0, 0] + m[1, 1] + m[2, 2] + m[3, 3] (but m[3, 3] == 1)
T s = std::sqrt(trace + 1.0f); T s = std::sqrt(trace + 1.0f);
w = s / 2.0f; w = s / 2.0f;
s = 0.5f / s; s = 0.5f / s;
v.x = (m.m[2][1] - m.m[1][2]) * s; v.x = (m(2, 1) - m(1, 2)) * s;
v.y = (m.m[0][2] - m.m[2][0]) * s; v.y = (m(0, 2) - m(2, 0)) * s;
v.z = (m.m[1][0] - m.m[0][1]) * s; v.z = (m(1, 0) - m(0, 1)) * s;
} else { } else {
// Compute largest of $x$, $y$, or $z$, then remaining components // Compute largest of $x$, $y$, or $z$, then remaining components
const int nxt[3] = {1, 2, 0}; const int nxt[3] = {1, 2, 0};
T q[3]; T q[3];
int i = 0; int i = 0;
if (m.m[1][1] > m.m[0][0]) i = 1; if (m(1, 1) > m(0, 0)) i = 1;
if (m.m[2][2] > m.m[i][i]) i = 2; if (m(2, 2) > m(i, i)) i = 2;
int j = nxt[i]; int j = nxt[i];
int k = nxt[j]; int k = nxt[j];
T s = std::sqrt((m.m[i][i] - (m.m[j][j] + m.m[k][k])) + 1.0); T s = std::sqrt((m(i, i) - (m(j, j) + m(k, k))) + 1.0f);
q[i] = s * 0.5f; q[i] = s * 0.5f;
if (s != 0.f) s = 0.5f / s; if (s != 0.f) s = 0.5f / s;
w = (m.m[k][j] - m.m[j][k]) * s; w = (m(k, j) - m(j, k)) * s;
q[j] = (m.m[j][i] + m.m[i][j]) * s; q[j] = (m(j, i) + m(i, j)) * s;
q[k] = (m.m[k][i] + m.m[i][k]) * s; q[k] = (m(k, i) + m(i, k)) * s;
v.x = q[0]; v.x = q[0];
v.y = q[1]; v.y = q[1];
v.z = q[2]; v.z = q[2];

View File

@ -20,120 +20,5 @@
#if !defined(__MITSUBA_CORE_STL_H_) #if !defined(__MITSUBA_CORE_STL_H_)
#define __MITSUBA_CORE_STL_H_ #define __MITSUBA_CORE_STL_H_
namespace mitsuba { /// \endcond
namespace math {
#if defined(__LINUX__) && defined(__x86_64__)
/*
The Linux/x86_64 single precision implementations of 'exp'
and 'log' suffer from a serious performance regression.
It is about 5x faster to use the double-precision versions
with the extra overhead of the involved FP conversion.
Until this is fixed, the following aliases make sure that
the fastest implementation is used in every case.
*/
inline float fastexp(float value) {
return (float) ::exp((double) value);
}
inline double fastexp(double value) {
return ::exp(value);
}
inline float fastlog(float value) {
return (float) ::log((double) value);
}
inline double fastlog(double value) {
return ::log(value);
}
#else
inline float fastexp(float value) {
return ::expf(value);
}
inline double fastexp(double value) {
return ::exp(value);
}
inline float fastlog(float value) {
return ::logf(value);
}
inline double fastlog(double value) {
return ::log(value);
}
#endif
#if defined(_GNU_SOURCE)
inline void sincos(float theta, float *sin, float *cos) {
::sincosf(theta, sin, cos);
}
inline void sincos(double theta, double *sin, double *cos) {
::sincos(theta, sin, cos);
}
#else
inline void sincos(float theta, float *_sin, float *_cos) {
*_sin = sinf(theta);
*_cos = cosf(theta);
}
inline void sincos(double theta, double *_sin, double *_cos) {
*_sin = sin(theta);
*_cos = cos(theta);
}
#endif
/// Arcsine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline float safe_asin(float value) {
return std::asin(std::min(1.0f, std::max(-1.0f, value)));
}
/// Arcsine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline double safe_asin(double value) {
return std::asin(std::min(1.0, std::max(-1.0, value)));
}
/// Arccosine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline float safe_acos(float value) {
return std::acos(std::min(1.0f, std::max(-1.0f, value)));
}
/// Arccosine variant that gracefully handles arguments > 1 that are due to roundoff errors
inline double safe_acos(double value) {
return std::acos(std::min(1.0, std::max(-1.0, value)));
}
/// Square root variant that gracefully handles arguments < 0 that are due to roundoff errors
inline float safe_sqrt(float value) {
return std::sqrt(std::max(0.0f, value));
}
/// Square root variant that gracefully handles arguments < 0 that are due to roundoff errors
inline double safe_sqrt(double value) {
return std::sqrt(std::max(0.0, value));
}
/// Simple signum function -- note that it returns the FP sign of the input (and never zero)
inline Float signum(Float value) {
#if defined(__WINDOWS__)
return (Float) _copysign(1.0, value);
#elif defined(SINGLE_PRECISION)
return copysignf((float) 1.0, value);
#elif defined(DOUBLE_PRECISION)
return copysign((double) 1.0, value);
#endif
}
}; /* namespace math */
}; /* namespace mitsuba */
#if defined(_MSC_VER)
extern "C" {
extern MTS_EXPORT_CORE float nextafterf(float x, float y);
extern MTS_EXPORT_CORE double nextafter(double x, double y);
};
#endif
/// @endcond
#endif /* __MITSUBA_CORE_STL_H_ */ #endif /* __MITSUBA_CORE_STL_H_ */

View File

@ -17,11 +17,12 @@
*/ */
#pragma once #pragma once
#if !defined(__MITSUBA_RENDER_TRACK_H_) #if !defined(__MITSUBA_CORE_TRACK_H_)
#define __MITSUBA_RENDER_TRACK_H_ #define __MITSUBA_CORE_TRACK_H_
#include <mitsuba/core/quat.h> #include <mitsuba/core/quat.h>
#include <mitsuba/core/simplecache.h> #include <mitsuba/core/simplecache.h>
#include <set>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -31,7 +32,7 @@ template <typename T> class AnimationTrack;
* \brief Base class of animation tracks * \brief Base class of animation tracks
* \ingroup librender * \ingroup librender
*/ */
class MTS_EXPORT_RENDER AbstractAnimationTrack : public Object { class MTS_EXPORT_CORE AbstractAnimationTrack : public Object {
template<typename T> friend class AnimationTrack; template<typename T> friend class AnimationTrack;
public: public:
enum EType { enum EType {
@ -65,6 +66,9 @@ public:
/// Serialize to a binary data stream /// Serialize to a binary data stream
virtual void serialize(Stream *stream) const = 0; virtual void serialize(Stream *stream) const = 0;
/// Clone this track
virtual AbstractAnimationTrack *clone() const = 0;
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
protected: protected:
AbstractAnimationTrack(EType type, size_t nKeyframes) AbstractAnimationTrack(EType type, size_t nKeyframes)
@ -82,9 +86,9 @@ protected:
*/ */
template <typename T> class AnimationTrack : public AbstractAnimationTrack { template <typename T> class AnimationTrack : public AbstractAnimationTrack {
public: public:
typedef T value_type; typedef T ValueType;
AnimationTrack(EType type, size_t nKeyframes) AnimationTrack(EType type, size_t nKeyframes = 0)
: AbstractAnimationTrack(type, nKeyframes), m_values(nKeyframes) { } : AbstractAnimationTrack(type, nKeyframes), m_values(nKeyframes) { }
AnimationTrack(EType type, Stream *stream) AnimationTrack(EType type, Stream *stream)
@ -95,11 +99,38 @@ public:
unserialize(stream, m_values[i]); unserialize(stream, m_values[i]);
} }
/// Copy constructor
AnimationTrack(const AnimationTrack *track)
: AbstractAnimationTrack(track->getType(), track->getSize()) {
m_times = track->m_times;
m_values = track->m_values;
}
/// Set the value of a certain keyframe /// Set the value of a certain keyframe
inline void setValue(size_t idx, const value_type &value) { m_values[idx] = value; } inline void setValue(size_t idx, const ValueType &value) { m_values[idx] = value; }
/// Return the value of a certain keyframe /// Return the value of a certain keyframe
inline const value_type &getValue(size_t idx) const { return m_values[idx]; } inline const ValueType &getValue(size_t idx) const { return m_values[idx]; }
/// Reserve space for a certain number of entries
inline void reserve(size_t count) { m_times.reserve(count); m_values.reserve(count); }
/// Append a value
inline void append(Float time, const ValueType &value) {
m_times.push_back(time);
m_values.push_back(value);
}
/// Clone this instance
AbstractAnimationTrack *clone() const {
return new AnimationTrack(this);
}
/// Prepend a transformation to every entry of this track
void prependTransformation(const ValueType &value) {
for (size_t i=0; i<m_values.size(); ++i)
m_values[i] = concatenateTransformations(m_values[i], value);
}
/// Serialize to a binary data stream /// Serialize to a binary data stream
inline void serialize(Stream *stream) const { inline void serialize(Stream *stream) const {
@ -111,7 +142,7 @@ public:
} }
/// Evaluate the animation track at an arbitrary time value /// Evaluate the animation track at an arbitrary time value
inline value_type eval(Float time) const { inline ValueType eval(Float time) const {
SAssert(m_times.size() > 0); SAssert(m_times.size() > 0);
std::vector<Float>::const_iterator entry = std::vector<Float>::const_iterator entry =
std::lower_bound(m_times.begin(), m_times.end(), time); std::lower_bound(m_times.begin(), m_times.end(), time);
@ -126,19 +157,84 @@ public:
} }
return lerp(idx0, idx1, t); return lerp(idx0, idx1, t);
} }
private:
struct SortPredicate {
inline bool operator()(const std::pair<Float, ValueType> &p1,
const std::pair<Float, ValueType> &p2) const {
return p1.first < p2.first;
}
};
struct UniqueTimePredicate {
inline bool operator()(const std::pair<Float, ValueType> &p1,
const std::pair<Float, ValueType> &p2) const {
return p1.first == p2.first;
}
};
public:
/**
* \brief Sort all animation tracks and remove
* unnecessary data (for user-provided input)
*
* \return \c false if this animation track was deemed to be "trivial"
* after the cleanup (for instance, it only contains (0,0,0) translation operations)
*/
bool sortAndSimplify() {
SAssert(m_values.size() == m_times.size());
if (m_values.size() == 0)
return false;
std::vector< std::pair<Float, ValueType> > temp(m_values.size());
for (size_t i=0; i<m_values.size(); ++i)
temp[i] = std::make_pair(m_times[i], m_values[i]);
std::sort(temp.begin(), temp.end(), SortPredicate());
m_times.clear(); m_values.clear();
m_times.push_back(temp[0].first);
m_values.push_back(temp[0].second);
for (size_t i=1; i<temp.size(); ++i) {
Float time = temp[i].first;
const ValueType &value = temp[i].second;
if (m_times.back() == time)
SLog(EError, "Duplicate time value in animated transformation!");
/* Ignore irrelevant keys */
if (i+1 < temp.size() && value == temp[i+1].second &&
value == m_values.back())
continue;
else if (i+1 == temp.size() && value == m_values.back())
continue;
m_times.push_back(time);
m_values.push_back(value);
}
return !(m_values.size() == 0 || (m_values.size() == 1 && isNoOp(m_values[0])));
}
protected: protected:
/// Evaluate the animation track using linear interpolation /// Evaluate the animation track using linear interpolation
inline value_type lerp(size_t idx0, size_t idx1, Float t) const; inline ValueType lerp(size_t idx0, size_t idx1, Float t) const;
inline void unserialize(Stream *stream, value_type &value) { /// Is this a "no-op" transformation?
value = stream->readElement<value_type>(); inline bool isNoOp(const ValueType &value) const;
/// Concatenate two transformations
inline ValueType concatenateTransformations(
const ValueType &value1, const ValueType &value2) const;
inline void unserialize(Stream *stream, ValueType &value) {
value = stream->readElement<ValueType>();
} }
inline void serialize(Stream *stream, const value_type &value) const { inline void serialize(Stream *stream, const ValueType &value) const {
stream->writeElement<value_type>(value); stream->writeElement<ValueType>(value);
} }
private: private:
std::vector<value_type> m_values; std::vector<ValueType> m_values;
}; };
template<typename T> inline T AnimationTrack<T>::lerp(size_t idx0, size_t idx1, Float t) const { template<typename T> inline T AnimationTrack<T>::lerp(size_t idx0, size_t idx1, Float t) const {
@ -150,6 +246,53 @@ template<> inline Quaternion AnimationTrack<Quaternion>::lerp(size_t idx0, size_
return slerp(m_values[idx0], m_values[idx1], t); return slerp(m_values[idx0], m_values[idx1], t);
} }
template<typename T> inline T AnimationTrack<T>::concatenateTransformations(
const T &value1, const T &value2) const {
return value1 * value2;
}
template<> inline Vector AnimationTrack<Vector>::concatenateTransformations(
const Vector &value1, const Vector &value2) const {
if (m_type == ETranslationXYZ)
return value1 + value2;
else
return Vector(value1.x * value2.x, value1.y * value2.y, value1.z * value2.z);
}
template<> inline Float AnimationTrack<Float>::concatenateTransformations(
const Float &value1, const Float &value2) const {
if (m_type == ETranslationX || m_type == ETranslationY || m_type == ETranslationZ)
return value1 + value2;
else
return value1 * value2;
}
template<typename T> inline bool AnimationTrack<T>::isNoOp(const ValueType &value) const {
return false;
}
template<> inline bool AnimationTrack<Float>::isNoOp(const Float &value) const {
if ((m_type == ETranslationX || m_type == ETranslationY || m_type == ETranslationZ) && value == 0)
return true;
else if ((m_type == ERotationX || m_type == ERotationY || m_type == ERotationZ) && value == 0)
return true;
else if ((m_type == EScaleX || m_type == EScaleY || m_type == EScaleZ) && value == 1)
return true;
return false;
}
template<> inline bool AnimationTrack<Vector>::isNoOp(const Vector &value) const {
if (m_type == ETranslationXYZ && value.isZero())
return true;
else if (m_type == EScaleXYZ && (value.x == 1 && value.y == 1 && value.z == 1))
return true;
return false;
}
template<> inline bool AnimationTrack<Quaternion>::isNoOp(const Quaternion &value) const {
return value.isIdentity();
}
template<> inline void AnimationTrack<Point>::unserialize(Stream *stream, Point &value) { template<> inline void AnimationTrack<Point>::unserialize(Stream *stream, Point &value) {
value = Point(stream); value = Point(stream);
} }
@ -178,8 +321,8 @@ template<> inline void AnimationTrack<Quaternion>::serialize(Stream *stream, con
* \brief Animated transformation with an underlying keyframe representation * \brief Animated transformation with an underlying keyframe representation
* \ingroup librender * \ingroup librender
*/ */
class MTS_EXPORT_RENDER AnimatedTransform : public Object { class MTS_EXPORT_CORE AnimatedTransform : public Object {
protected: private:
/// Internal functor used by \ref eval() and \ref SimpleCache /// Internal functor used by \ref eval() and \ref SimpleCache
struct MTS_EXPORT_RENDER TransformFunctor { struct MTS_EXPORT_RENDER TransformFunctor {
public: public:
@ -204,12 +347,27 @@ public:
/// Unserialized an animated transformation from a binary data stream /// Unserialized an animated transformation from a binary data stream
AnimatedTransform(Stream *stream); AnimatedTransform(Stream *stream);
/// Copy constructor
AnimatedTransform(const AnimatedTransform *trafo);
/// Return the number of associated animation tracks /// Return the number of associated animation tracks
inline size_t getTrackCount() const { return m_tracks.size(); } inline size_t getTrackCount() const { return m_tracks.size(); }
/// Find a track of the given type
AbstractAnimationTrack *findTrack(AbstractAnimationTrack::EType type);
/// Find a track of the given type
const AbstractAnimationTrack *findTrack(AbstractAnimationTrack::EType type) const;
/// Look up one of the tracks by index /// Look up one of the tracks by index
inline AbstractAnimationTrack *getTrack(size_t idx) { return m_tracks[idx]; }
/// Look up one of the tracks by index (const version)
inline const AbstractAnimationTrack *getTrack(size_t idx) const { return m_tracks[idx]; } inline const AbstractAnimationTrack *getTrack(size_t idx) const { return m_tracks[idx]; }
/// Return the used keyframes as a set
void collectKeyframes(std::set<Float> &result) const;
/// Append an animation track /// Append an animation track
void addTrack(AbstractAnimationTrack *track); void addTrack(AbstractAnimationTrack *track);
@ -221,7 +379,7 @@ public:
* to this function. * to this function.
*/ */
inline const Transform &eval(Float t) const { inline const Transform &eval(Float t) const {
if (m_tracks.size() == 0) if (EXPECT_TAKEN(m_tracks.size() == 0))
return m_transform; return m_transform;
else else
return m_cache.get(TransformFunctor(m_tracks), t); return m_cache.get(TransformFunctor(m_tracks), t);
@ -230,6 +388,12 @@ public:
/// Is the animation static? /// Is the animation static?
inline bool isStatic() const { return m_tracks.size() == 0; } inline bool isStatic() const { return m_tracks.size() == 0; }
/**
* \brief Sort all animation tracks and remove unnecessary
* data (for user-provided input)
*/
void sortAndSimplify();
/// Transform a point by an affine / non-projective matrix /// Transform a point by an affine / non-projective matrix
inline Point transformAffine(Float t, const Point &p) const { inline Point transformAffine(Float t, const Point &p) const {
return eval(t).transformAffine(p); return eval(t).transformAffine(p);
@ -290,6 +454,9 @@ public:
eval(t).operator()(r, dest); eval(t).operator()(r, dest);
} }
/// Prepend a scale transformation to the transform (this is often useful)
void prependScale(const Vector &scale);
/// Serialize to a binary data stream /// Serialize to a binary data stream
void serialize(Stream *stream) const; void serialize(Stream *stream) const;
@ -317,4 +484,4 @@ private:
MTS_NAMESPACE_END MTS_NAMESPACE_END
#endif /* __MITSUBA_RENDER_TRACK_H_ */ #endif /* __MITSUBA_CORE_TRACK_H_ */

View File

@ -110,6 +110,9 @@ extern MTS_EXPORT_CORE int getCoreCount();
/// Return the host name of this machine /// Return the host name of this machine
extern MTS_EXPORT_CORE std::string getHostName(); extern MTS_EXPORT_CORE std::string getHostName();
/// Return the process private memory usage in bytes
extern MTS_EXPORT_CORE size_t getPrivateMemoryUsage();
/// Return the fully qualified domain name of this machine /// Return the fully qualified domain name of this machine
extern MTS_EXPORT_CORE std::string getFQDN(); extern MTS_EXPORT_CORE std::string getFQDN();

View File

@ -26,13 +26,13 @@ MTS_NAMESPACE_BEGIN
* \brief Current release of Mitsuba * \brief Current release of Mitsuba
* \ingroup libcore * \ingroup libcore
*/ */
#define MTS_VERSION "0.4.2" #define MTS_VERSION "0.4.3"
/** /**
* \brief Year of the current release * \brief Year of the current release
* \ingroup libcore * \ingroup libcore
*/ */
#define MTS_YEAR "2012" #define MTS_YEAR "2013"
/** /**
* \brief A simple data structure for representing and * \brief A simple data structure for representing and

View File

@ -326,11 +326,14 @@ protected:
* number of GPU pipeline flushes. Draw transparent objects last. * number of GPU pipeline flushes. Draw transparent objects last.
*/ */
struct MaterialOrder { struct MaterialOrder {
inline bool operator()( const std::vector<Renderer::TransformedGPUGeometry> &geo;
const Renderer::TransformedGPUGeometry &g1,
const Renderer::TransformedGPUGeometry &g2) const { MaterialOrder(const std::vector<Renderer::TransformedGPUGeometry> &geo)
const Shader *shader1 = g1.first->getShader(); : geo(geo) { }
const Shader *shader2 = g2.first->getShader();
inline bool operator()(size_t idx1, size_t idx2) const {
const Shader *shader1 = geo[idx1].first->getShader();
const Shader *shader2 = geo[idx2].first->getShader();
if (shader1 && (shader1->getFlags() & Shader::ETransparent)) if (shader1 && (shader1->getFlags() & Shader::ETransparent))
shader1 = NULL; shader1 = NULL;
@ -340,6 +343,19 @@ protected:
return shader1 < shader2; return shader1 < shader2;
} }
}; };
/// Helper data structure to keep track of shapes that are undergoing keyframe animations
struct AnimatedGeometryRecord {
const AnimatedTransform *trafo;
ssize_t geometryIndex;
ssize_t opaqueGeometryIndex;
AnimatedGeometryRecord(const AnimatedTransform *trafo,
ssize_t geometryIndex, ssize_t opaqueGeometryIndex) :
trafo(trafo), geometryIndex(geometryIndex),
opaqueGeometryIndex(opaqueGeometryIndex) { }
};
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
ref<Renderer> m_renderer; ref<Renderer> m_renderer;
@ -348,6 +364,7 @@ private:
/* On-GPU geometry references */ /* On-GPU geometry references */
std::vector<Renderer::TransformedGPUGeometry> m_geometry; std::vector<Renderer::TransformedGPUGeometry> m_geometry;
std::vector<Renderer::TransformedGPUGeometry> m_opaqueGeometry; std::vector<Renderer::TransformedGPUGeometry> m_opaqueGeometry;
std::vector<AnimatedGeometryRecord> m_animatedGeometry;
/* Shader & dependency management */ /* Shader & dependency management */
std::map<std::string, VPLConfiguration> m_configurations; std::map<std::string, VPLConfiguration> m_configurations;

View File

@ -46,6 +46,7 @@ using std::endl;
#include <mitsuba/core/fwd.h> #include <mitsuba/core/fwd.h>
#include <mitsuba/render/fwd.h> #include <mitsuba/render/fwd.h>
#include <mitsuba/core/stl.h> #include <mitsuba/core/stl.h>
#include <mitsuba/core/math.h>
#include <mitsuba/core/object.h> #include <mitsuba/core/object.h>
#include <mitsuba/core/ref.h> #include <mitsuba/core/ref.h>
#include <mitsuba/core/tls.h> #include <mitsuba/core/tls.h>

View File

@ -21,7 +21,7 @@
#define __MITSUBA_RENDER_EMITTER_H_ #define __MITSUBA_RENDER_EMITTER_H_
#include <mitsuba/render/common.h> #include <mitsuba/render/common.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
#include <mitsuba/core/cobject.h> #include <mitsuba/core/cobject.h>
#include <mitsuba/render/shader.h> #include <mitsuba/render/shader.h>
@ -425,7 +425,7 @@ protected:
/// Virtual destructor /// Virtual destructor
virtual ~AbstractEmitter(); virtual ~AbstractEmitter();
protected: protected:
ref<AnimatedTransform> m_worldTransform; ref<const AnimatedTransform> m_worldTransform;
ref<Medium> m_medium; ref<Medium> m_medium;
Shape *m_shape; Shape *m_shape;
uint32_t m_type; uint32_t m_type;

View File

@ -61,7 +61,7 @@ public:
virtual void setDestinationFile(const fs::path &filename, uint32_t blockSize) = 0; virtual void setDestinationFile(const fs::path &filename, uint32_t blockSize) = 0;
/// Develop the film and write the result to the previously specified filename /// Develop the film and write the result to the previously specified filename
virtual void develop() = 0; virtual void develop(const Scene *scene, Float renderTime) = 0;
/** /**
* \brief Develop the contents of a subregion of the film and store * \brief Develop the contents of a subregion of the film and store

View File

@ -1321,7 +1321,9 @@ protected:
return a.axis < b.axis; return a.axis < b.axis;
if (a.pos != b.pos) if (a.pos != b.pos)
return a.pos < b.pos; return a.pos < b.pos;
return a.type < b.type; if (a.type != b.type)
return a.type < b.type;
return a.index < b.index;
} }
}; };

View File

@ -75,7 +75,7 @@ public:
bool interactive = false); bool interactive = false);
/// Write out the current (partially rendered) image /// Write out the current (partially rendered) image
inline void flush() { m_scene->flush(); } inline void flush() { m_scene->flush(m_queue, this); }
/// Cancel a running render job /// Cancel a running render job
inline void cancel() { m_scene->cancel(); } inline void cancel() { m_scene->cancel(); }

View File

@ -76,6 +76,9 @@ public:
/// Remove a (finished) render job from the queue /// Remove a (finished) render job from the queue
void removeJob(RenderJob *thr, bool wasCancelled); void removeJob(RenderJob *thr, bool wasCancelled);
/// Return the amount of time spent rendering the given job (in seconds)
Float getRenderTime(const RenderJob *job) const;
/// Register a render listener /// Register a render listener
void registerListener(RenderListener *listener); void registerListener(RenderListener *listener);

View File

@ -63,7 +63,8 @@ public:
* Given a split on axis \a axis that produces children having extents * Given a split on axis \a axis that produces children having extents
* \a leftWidth and \a rightWidth along \a axis, compute the probability * \a leftWidth and \a rightWidth along \a axis, compute the probability
* of traversing the left and right child during a typical query * of traversing the left and right child during a typical query
* operation. * operation. In the case of the surface area heuristic, this is simply
* the ratio of surface areas.
*/ */
inline std::pair<Float, Float> operator()(int axis, Float leftWidth, Float rightWidth) const { inline std::pair<Float, Float> operator()(int axis, Float leftWidth, Float rightWidth) const {
return std::pair<Float, Float>( return std::pair<Float, Float>(

View File

@ -0,0 +1,304 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__SAH_KDTREE4_H)
#define __SAH_KDTREE4_H
#include <mitsuba/render/sahkdtree3.h>
MTS_NAMESPACE_BEGIN
typedef TAABB<Point4> AABB4;
/**
* \brief Implements the four-dimensional surface area heuristic for use
* by the \ref GenericKDTree construction algorithm.
*/
class SurfaceAreaHeuristic4 {
public:
/**
* \brief Initialize the surface area heuristic with the bounds of
* a parent node
*
* Precomputes some information so that traversal probabilities
* of potential split planes can be evaluated efficiently
*/
inline SurfaceAreaHeuristic4(const AABB4 &aabb) : m_aabb(aabb) {
const Vector4 extents(aabb.getExtents());
const Float temp = 1.0f / (extents.x * extents.y
+ extents.y*extents.z + extents.x*extents.z);
m_temp0 = Vector4(
extents.y * extents.z * temp,
extents.x * extents.z * temp,
extents.x * extents.y * temp,
0.0f);
m_temp1 = Vector4(
(extents.y + extents.z) * temp,
(extents.x + extents.z) * temp,
(extents.x + extents.y) * temp,
1.0f / extents.w);
}
/**
* Given a split on axis \a axis that produces children having extents
* \a leftWidth and \a rightWidth along \a axis, compute the probability
* of traversing the left and right child during a typical query
* operation.
*/
inline std::pair<Float, Float> operator()(int axis, Float leftWidth, Float rightWidth) const {
if (axis == 3 && m_temp1.w == std::numeric_limits<Float>::infinity()) {
return std::pair<Float, Float>(
std::numeric_limits<Float>::infinity(),
std::numeric_limits<Float>::infinity()
);
}
return std::pair<Float, Float>(
m_temp0[axis] + m_temp1[axis] * leftWidth,
m_temp0[axis] + m_temp1[axis] * rightWidth);
}
/**
* Compute the underlying quantity used by the tree construction
* heuristic. This is used to compute the final cost of a kd-tree.
*/
inline static Float getQuantity(const AABB4 &aabb) {
const Vector4 extents(aabb.getExtents());
Float result = 2 * (extents[0] * extents[1] + extents[1] * extents[2]
+ extents[2] * extents[0]);
if (extents[3] != 0)
result *= extents[3];
return result;
}
private:
Vector4 m_temp0, m_temp1;
AABB4 m_aabb;
};
/**
* This class specializes \ref GenericKDTree to a four-dimensional
* tree to be used for spacetime ray tracing. One additional function call
* must be implemented by subclasses:
*
* /// Check whether a primitive is intersected by the given ray.
* /// Some temporary space is supplied, which can be used to cache
* /// information about the intersection
* bool intersect(const Ray &ray, IndexType idx,
* Float mint, Float maxt, Float &t, void *tmp);
*
* This class implements an epsilon-free version of the optimized ray
* traversal algorithm (TA^B_{rec}), which is explained in Vlastimil
* Havran's PhD thesis "Heuristic Ray Shooting Algorithms".
*
* \author Wenzel Jakob
*/
template <typename Derived>
class SAHKDTree4D : public GenericKDTree<AABB4, SurfaceAreaHeuristic4, Derived> {
public:
typedef typename KDTreeBase<AABB4>::SizeType SizeType;
typedef typename KDTreeBase<AABB4>::IndexType IndexType;
typedef typename KDTreeBase<AABB4>::KDNode KDNode;
protected:
void buildInternal() {
SizeType primCount = this->cast()->getPrimitiveCount();
KDLog(EInfo, "Constructing a 4D SAH kd-tree (%i primitives) ..", primCount);
GenericKDTree<AABB4, SurfaceAreaHeuristic4, Derived>::buildInternal();
}
/**
* \brief Hashed mailbox implementation
*/
struct HashedMailbox {
inline HashedMailbox() {
memset(entries, 0xFF, sizeof(IndexType)*MTS_KD_MAILBOX_SIZE);
}
inline void put(IndexType primIndex) {
entries[primIndex & MTS_KD_MAILBOX_MASK] = primIndex;
}
inline bool contains(IndexType primIndex) const {
return entries[primIndex & MTS_KD_MAILBOX_MASK] == primIndex;
}
IndexType entries[MTS_KD_MAILBOX_SIZE];
};
/// Ray traversal stack entry for Havran-style incoherent ray tracing
struct KDStackEntryHavran {
/* Pointer to the far child */
const KDNode * __restrict node;
/* Distance traveled along the ray (entry or exit) */
Float t;
/* Previous stack item */
uint32_t prev;
/* Associated point */
Point p;
};
/**
* \brief Ray tracing kd-tree traversal loop (Havran variant)
*
* This is generally the most robust and fastest traversal routine
* of the methods implemented in this class.
*/
template<bool shadowRay> FINLINE
bool rayIntersectHavran(const Ray &ray, Float mint, Float maxt,
Float &t, void *temp) const {
KDStackEntryHavran stack[MTS_KD_MAXDEPTH];
#if 0
static const int prevAxisTable[] = { 2, 0, 1 };
static const int nextAxisTable[] = { 1, 2, 0 };
#endif
#if defined(MTS_KD_MAILBOX_ENABLED)
HashedMailbox mailbox;
#endif
/* Set up the entry point */
uint32_t enPt = 0;
stack[enPt].t = mint;
stack[enPt].p = ray(mint);
/* Set up the exit point */
uint32_t exPt = 1;
stack[exPt].t = maxt;
stack[exPt].p = ray(maxt);
stack[exPt].node = NULL;
bool foundIntersection = false;
const KDNode * __restrict currNode = this->m_nodes;
while (currNode != NULL) {
while (EXPECT_TAKEN(!currNode->isLeaf())) {
const Float splitVal = (Float) currNode->getSplit();
const int axis = currNode->getAxis();
const KDNode * __restrict farChild;
if (axis == 3) {
if (ray.time <= splitVal)
currNode = currNode->getLeft();
else
currNode = currNode->getRight();
continue;
} else if (stack[enPt].p[axis] <= splitVal) {
if (stack[exPt].p[axis] <= splitVal) {
/* Cases N1, N2, N3, P5, Z2 and Z3 (see thesis) */
currNode = currNode->getLeft();
continue;
}
/* Typo in Havran's thesis:
(it specifies "stack[exPt].p == splitVal", which
is clearly incorrect) */
if (stack[enPt].p[axis] == splitVal) {
/* Case Z1 */
currNode = currNode->getRight();
continue;
}
/* Case N4 */
currNode = currNode->getLeft();
farChild = currNode + 1; // getRight()
} else { /* stack[enPt].p[axis] > splitVal */
if (splitVal < stack[exPt].p[axis]) {
/* Cases P1, P2, P3 and N5 */
currNode = currNode->getRight();
continue;
}
/* Case P4 */
farChild = currNode->getLeft();
currNode = farChild + 1; // getRight()
}
/* Cases P4 and N4 -- calculate the distance to the split plane */
Float distToSplit = (splitVal - ray.o[axis]) * ray.dRcp[axis];
/* Set up a new exit point */
const uint32_t tmp = exPt++;
if (exPt == enPt) /* Do not overwrite the entry point */
++exPt;
KDAssert(exPt < MTS_KD_MAXDEPTH);
stack[exPt].prev = tmp;
stack[exPt].t = distToSplit;
stack[exPt].node = farChild;
#if 1
/* Intrestingly, this appears to be faster than the
original code with the prevAxis & nextAxis table */
stack[exPt].p = ray(distToSplit);
stack[exPt].p[axis] = splitVal;
#else
const int nextAxis = nextAxisTable[axis];
const int prevAxis = prevAxisTable[axis];
stack[exPt].p[axis] = splitVal;
stack[exPt].p[nextAxis] = ray.o[nextAxis]
+ distToSplit*ray.d[nextAxis];
stack[exPt].p[prevAxis] = ray.o[prevAxis]
+ distToSplit*ray.d[prevAxis];
#endif
}
/* Reached a leaf node */
for (IndexType entry=currNode->getPrimStart(),
last = currNode->getPrimEnd(); entry != last; entry++) {
const IndexType primIdx = this->m_indices[entry];
#if defined(MTS_KD_MAILBOX_ENABLED)
if (mailbox.contains(primIdx))
continue;
#endif
bool result;
if (!shadowRay)
result = this->cast()->intersect(ray, primIdx, mint, maxt, t, temp);
else
result = this->cast()->intersect(ray, primIdx, mint, maxt);
if (result) {
if (shadowRay)
return true;
maxt = t;
foundIntersection = true;
}
#if defined(MTS_KD_MAILBOX_ENABLED)
mailbox.put(primIdx);
#endif
}
if (stack[exPt].t > maxt)
break;
/* Pop from the stack and advance to the next node on the interval */
enPt = exPt;
currNode = stack[exPt].node;
exPt = stack[enPt].prev;
}
return foundIntersection;
}
};
MTS_NAMESPACE_END
#endif /* __SAH_KDTREE4_H */

View File

@ -133,7 +133,7 @@ public:
int sceneResID, int sensorResID, int samplerResID); int sceneResID, int sensorResID, int samplerResID);
/// Write out the current (partially rendered) image /// Write out the current (partially rendered) image
void flush(); void flush(RenderQueue *queue, const RenderJob *job);
/** /**
* \brief Cancel a running rendering job * \brief Cancel a running rendering job

View File

@ -158,7 +158,8 @@ private:
EBoolean, EString, ETranslate, ERotate, EBoolean, EString, ETranslate, ERotate,
ELookAt, EScale, EMatrix, EPoint, ELookAt, EScale, EMatrix, EPoint,
EVector, ERGB, ESRGB, EBlackBody, EVector, ERGB, ESRGB, EBlackBody,
ESpectrum, ETransform, EInclude, EAlias ESpectrum, ETransform, EAnimation,
EInclude, EAlias
}; };
typedef std::pair<ETag, const Class *> TagEntry; typedef std::pair<ETag, const Class *> TagEntry;
@ -173,6 +174,7 @@ private:
std::stack<ParseContext> m_context; std::stack<ParseContext> m_context;
TagMap m_tags; TagMap m_tags;
Transform m_transform; Transform m_transform;
ref<AnimatedTransform> m_animatedTransform;
bool m_isIncludedFile; bool m_isIncludedFile;
}; };

View File

@ -193,6 +193,9 @@ public:
/** /**
* \brief Return the shape's surface area * \brief Return the shape's surface area
* *
* Assumes that the object is not undergoing some kind of
* time-dependent scaling.
*
* The default implementation throws an exception * The default implementation throws an exception
*/ */
virtual Float getSurfaceArea() const; virtual Float getSurfaceArea() const;

View File

@ -32,9 +32,9 @@
#if defined(SINGLE_PRECISION) #if defined(SINGLE_PRECISION)
/// 32 byte temporary storage for intersection computations /// 32 byte temporary storage for intersection computations
#define MTS_KD_INTERSECTION_TEMP 32
#else
#define MTS_KD_INTERSECTION_TEMP 64 #define MTS_KD_INTERSECTION_TEMP 64
#else
#define MTS_KD_INTERSECTION_TEMP 128
#endif #endif
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN

View File

@ -22,7 +22,7 @@
#include <mitsuba/core/fstream.h> #include <mitsuba/core/fstream.h>
#include <mitsuba/core/timer.h> #include <mitsuba/core/timer.h>
#include <mitsuba/core/version.h> #include <mitsuba/core/version.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <dom/domCOLLADA.h> #include <dom/domCOLLADA.h>
#include <dae.h> #include <dae.h>
#include <dae/daeErrorHandler.h> #include <dae/daeErrorHandler.h>

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/emitter.h> #include <mitsuba/render/emitter.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/emitter.h> #include <mitsuba/render/emitter.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/hw/gpuprogram.h> #include <mitsuba/hw/gpuprogram.h>
#include <mitsuba/core/warp.h> #include <mitsuba/core/warp.h>

View File

@ -71,6 +71,10 @@ MTS_NAMESPACE_BEGIN
* Specifies the relative amount of samples * Specifies the relative amount of samples
* allocated to this emitter. \default{1} * allocated to this emitter. \default{1}
* } * }
* \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional sensor-to-world transformation.
* \default{none (i.e. sensor space $=$ world space)}
* }
* } * }
* *
* \renderings{ * \renderings{
@ -376,8 +380,7 @@ public:
bitmapData.ptr = (uint8_t *) bitmap.get(); bitmapData.ptr = (uint8_t *) bitmap.get();
bitmapData.size = sizeof(Bitmap); bitmapData.size = sizeof(Bitmap);
props.setData("bitmap", bitmapData); props.setData("bitmap", bitmapData);
const Transform &trafo = m_worldTransform->eval(0); props.setAnimatedTransform("toWorld", m_worldTransform);
props.setTransform("toWorld", trafo);
props.setFloat("samplingWeight", m_samplingWeight); props.setFloat("samplingWeight", m_samplingWeight);
Emitter *emitter = static_cast<Emitter *>( Emitter *emitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(

View File

@ -221,8 +221,7 @@ public:
bitmapData.ptr = (uint8_t *) bitmap.get(); bitmapData.ptr = (uint8_t *) bitmap.get();
bitmapData.size = sizeof(Bitmap); bitmapData.size = sizeof(Bitmap);
props.setData("bitmap", bitmapData); props.setData("bitmap", bitmapData);
const Transform &trafo = m_worldTransform->eval(0); props.setAnimatedTransform("toWorld", m_worldTransform);
props.setTransform("toWorld", trafo);
props.setFloat("samplingWeight", m_samplingWeight); props.setFloat("samplingWeight", m_samplingWeight);
Emitter *emitter = static_cast<Emitter *>( Emitter *emitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(

View File

@ -223,7 +223,7 @@ public:
bitmapData.ptr = (uint8_t *) bitmap.get(); bitmapData.ptr = (uint8_t *) bitmap.get();
bitmapData.size = sizeof(Bitmap); bitmapData.size = sizeof(Bitmap);
envProps.setData("bitmap", bitmapData); envProps.setData("bitmap", bitmapData);
envProps.setTransform("toWorld", trafo); envProps.setAnimatedTransform("toWorld", m_worldTransform);
envProps.setFloat("samplingWeight", m_samplingWeight); envProps.setFloat("samplingWeight", m_samplingWeight);
m_envEmitter = static_cast<Emitter *>( m_envEmitter = static_cast<Emitter *>(
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(

View File

@ -8,8 +8,8 @@ macro(add_film)
endmacro() endmacro()
add_film(mfilm mfilm.cpp) add_film(mfilm mfilm.cpp)
add_film(ldrfilm ldrfilm.cpp banner.h MTS_HW) add_film(ldrfilm ldrfilm.cpp annotations.h banner.h MTS_HW)
add_film(hdrfilm hdrfilm.cpp banner.h MTS_HW) add_film(hdrfilm hdrfilm.cpp annotations.h banner.h MTS_HW)
if (OPENEXR_FOUND) if (OPENEXR_FOUND)
include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS}) include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS})

137
src/films/annotations.h Normal file
View File

@ -0,0 +1,137 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__ANNOTATIONS_H)
#define __ANNOTATIONS_H
#include <mitsuba/hw/font.h>
#include <mitsuba/render/scene.h>
MTS_NAMESPACE_BEGIN
/**
* This function implements a parser for the 'label[]' and 'metadata[]'
* annotations supported by the ldrfilm and hdrfilm plugins
*/
void annotate(const Scene *scene, const Properties &properties,
Bitmap *bitmap, Float renderTime, Float gamma) {
/* Attach the custom annotations */
Properties &metadata = bitmap->getMetadata();
std::vector<std::string> keys = properties.getPropertyNames();
ref<Font> font;
for (size_t i=0; i<keys.size(); ++i) {
std::string key = keys[i];
key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
std::string lkey = boost::to_lower_copy(key);
Point2i offset(0, 0);
bool labelAnnotation = false;
if (boost::starts_with(lkey, "metadata['") && boost::ends_with(lkey, "']")) {
key = key.substr(10, key.length()-12);
} else if (boost::starts_with(lkey, "label[") && boost::ends_with(lkey, "]")) {
std::vector<std::string> args = tokenize(key.substr(6, key.length()-7), " ,");
if (args.size() != 2)
SLog(EError, "Label command '%s' has an invalid number of arguments!", key.c_str());
char *end_ptr = NULL;
offset.x = strtol(args[0].c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
SLog(EError, "Label command '%s' has an invalid position argument!", key.c_str());
offset.y = strtol(args[1].c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
SLog(EError, "Label command '%s' has an invalid position argument!", key.c_str());
labelAnnotation = true;
if (font == NULL) {
font = new Font(Font::EBitstreamVeraMono14);
font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), gamma);
}
} else {
continue;
}
Properties::EPropertyType type = properties.getType(keys[i]);
if (type == Properties::EString) {
std::string value = properties.getString(keys[i]);
while (true) {
char *strt;
if (!(strt = strchr((char *) value.c_str(), '$')))
break;
if (strncasecmp(strt, "$rendertime", 11) == 0) {
value.replace(strt-value.c_str(), 11, timeString(renderTime));
} else if (strncasecmp(strt, "$memusage", 11) == 0) {
value.replace(strt-value.c_str(), 11, memString(getPrivateMemoryUsage()));
} else {
char *par1, *par2;
if (!(par1 = strchr(strt, '[')))
break;
if (!(par2 = strchr(par1, ']')))
break;
std::string propSource = value.substr(strt-value.c_str()+1, par1-strt-1);
std::string propKey = value.substr(par1-value.c_str()+1, par2-par1-1);
propSource.erase(std::remove_if(propSource.begin(), propSource.end(), ::isspace), propSource.end());
propKey.erase(std::remove_if(propKey.begin(), propKey.end(), ::isspace), propKey.end());
if (!boost::starts_with(propKey, "'") || !boost::ends_with(propKey, "'"))
SLog(EError, "Encountered invalid key '%s'", propKey.c_str());
propKey = propKey.substr(1, propKey.length()-2);
const ConfigurableObject *source = NULL;
if (propSource == "film")
source = scene->getFilm();
else if (propSource == "sampler")
source = scene->getSampler();
else if (propSource == "sensor")
source = scene->getSensor();
else if (propSource == "integrator")
source = scene->getIntegrator();
else
SLog(EError, "Unknown data source '%s' (must be film/sampler/sensor/integrator)", propSource.c_str());
std::string replacement;
if (propKey == "type")
replacement = source->getProperties().getPluginName();
else
replacement = source->getProperties().getAsString(propKey);
value.replace(strt-value.c_str(), par2-strt+1, replacement);
}
}
if (labelAnnotation) {
Vector2i size = font->getSize(value);
bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
font->drawText(bitmap, offset, value);
} else {
metadata.setString(key, value);
}
} else {
if (labelAnnotation)
SLog(EError, "Only string-valued label annotations are supported!");
metadata.copyAttribute(properties, keys[i], key);
}
}
}
MTS_NAMESPACE_END
#endif /* __ANNOTATIONS_H */

View File

@ -20,9 +20,9 @@
#include <mitsuba/core/fstream.h> #include <mitsuba/core/fstream.h>
#include <mitsuba/core/bitmap.h> #include <mitsuba/core/bitmap.h>
#include <mitsuba/core/statistics.h> #include <mitsuba/core/statistics.h>
#include <mitsuba/hw/font.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "banner.h" #include "banner.h"
#include "annotations.h"
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -235,20 +235,11 @@ public:
std::vector<std::string> keys = props.getPropertyNames(); std::vector<std::string> keys = props.getPropertyNames();
for (size_t i=0; i<keys.size(); ++i) { for (size_t i=0; i<keys.size(); ++i) {
std::string key = boost::to_lower_copy(keys[i]); std::string key = boost::to_lower_copy(keys[i]);
key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
if (boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) { if ((boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) ||
m_tags[keys[i].substr(5, key.length()-7)] = props.getString(keys[i]); (boost::starts_with(key, "text(") && boost::ends_with(key, ")")))
} else if (boost::starts_with(key, "text(") && boost::ends_with(key, ")")) { props.markQueried(keys[i]);
std::vector<std::string> args = tokenize(key.substr(5, key.length()-6), " ,");
if (args.size() != 2)
Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str());
Annotation annotation;
annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str()));
annotation.text = props.getString(keys[i]);
m_annotations.push_back(annotation);
}
} }
m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize); m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize);
@ -350,7 +341,7 @@ public:
m_destFile = destFile; m_destFile = destFile;
} }
void develop() { void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film .."); Log(EDebug, "Developing film ..");
ref<Bitmap> bitmap = m_storage->getBitmap()->convert( ref<Bitmap> bitmap = m_storage->getBitmap()->convert(
@ -368,23 +359,6 @@ public:
} }
} }
if (!m_annotations.empty()) {
ref<Font> font = new Font(Font::EBitstreamVeraMono14);
font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), 1.0f);
for (size_t i=0; i<m_annotations.size(); ++i) {
const Point2i &offset = m_annotations[i].offset;
const std::string &text = m_annotations[i].text;
Vector2i size = font->getSize(text);
bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
font->drawText(bitmap, offset, text);
}
}
for (std::map<std::string, std::string>::const_iterator it = m_tags.begin();
it != m_tags.end(); ++it)
bitmap->setMetadataString(it->first, it->second);
fs::path filename = m_destFile; fs::path filename = m_destFile;
std::string extension = boost::to_lower_copy(filename.extension().string()); std::string extension = boost::to_lower_copy(filename.extension().string());
std::string properExtension = (m_fileFormat == Bitmap::EOpenEXR) ? ".exr" : ".rgbe"; std::string properExtension = (m_fileFormat == Bitmap::EOpenEXR) ? ".exr" : ".rgbe";
@ -394,6 +368,8 @@ public:
Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str()); Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str());
ref<FileStream> stream = new FileStream(filename, FileStream::ETruncWrite); ref<FileStream> stream = new FileStream(filename, FileStream::ETruncWrite);
annotate(scene, m_properties, bitmap, renderTime, 1.0f);
/* Attach the log file to the image if this is requested */ /* Attach the log file to the image if this is requested */
Logger *logger = Thread::getThread()->getLogger(); Logger *logger = Thread::getThread()->getLogger();
std::string log; std::string log;
@ -439,11 +415,6 @@ public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
protected: protected:
struct Annotation {
Point2i offset;
std::string text;
};
Bitmap::EFileFormat m_fileFormat; Bitmap::EFileFormat m_fileFormat;
Bitmap::EPixelFormat m_pixelFormat; Bitmap::EPixelFormat m_pixelFormat;
Bitmap::EComponentFormat m_componentFormat; Bitmap::EComponentFormat m_componentFormat;
@ -451,9 +422,6 @@ protected:
bool m_attachLog; bool m_attachLog;
fs::path m_destFile; fs::path m_destFile;
ref<ImageBlock> m_storage; ref<ImageBlock> m_storage;
std::vector<Annotation> m_annotations;
std::map<std::string, std::string> m_tags;
}; };
MTS_IMPLEMENT_CLASS_S(HDRFilm, false, Film) MTS_IMPLEMENT_CLASS_S(HDRFilm, false, Film)

View File

@ -20,9 +20,9 @@
#include <mitsuba/core/fstream.h> #include <mitsuba/core/fstream.h>
#include <mitsuba/core/bitmap.h> #include <mitsuba/core/bitmap.h>
#include <mitsuba/core/statistics.h> #include <mitsuba/core/statistics.h>
#include <mitsuba/hw/font.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "banner.h" #include "banner.h"
#include "annotations.h"
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -185,20 +185,11 @@ public:
std::vector<std::string> keys = props.getPropertyNames(); std::vector<std::string> keys = props.getPropertyNames();
for (size_t i=0; i<keys.size(); ++i) { for (size_t i=0; i<keys.size(); ++i) {
std::string key = boost::to_lower_copy(keys[i]); std::string key = boost::to_lower_copy(keys[i]);
key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
if (boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) { if ((boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) ||
m_tags[keys[i].substr(5, key.length()-7)] = props.getString(keys[i]); (boost::starts_with(key, "text(") && boost::ends_with(key, ")")))
} else if (boost::starts_with(key, "text(") && boost::ends_with(key, ")")) { props.markQueried(keys[i]);
std::vector<std::string> args = tokenize(key.substr(5, key.length()-6), " ,");
if (args.size() != 2)
Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str());
Annotation annotation;
annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str()));
annotation.text = props.getString(keys[i]);
m_annotations.push_back(annotation);
}
} }
m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize); m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize);
@ -306,7 +297,7 @@ public:
m_destFile = destFile; m_destFile = destFile;
} }
void develop() { void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film .."); Log(EDebug, "Developing film ..");
ref<Bitmap> bitmap = m_storage->getBitmap(); ref<Bitmap> bitmap = m_storage->getBitmap();
@ -338,23 +329,6 @@ public:
} }
} }
if (!m_annotations.empty()) {
ref<Font> font = new Font(Font::EBitstreamVeraMono14);
font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), m_gamma);
for (size_t i=0; i<m_annotations.size(); ++i) {
const Point2i &offset = m_annotations[i].offset;
const std::string &text = m_annotations[i].text;
Vector2i size = font->getSize(text);
bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
font->drawText(bitmap, offset, text);
}
}
for (std::map<std::string, std::string>::const_iterator it = m_tags.begin();
it != m_tags.end(); ++it)
bitmap->setMetadataString(it->first, it->second);
fs::path filename = m_destFile; fs::path filename = m_destFile;
std::string extension = boost::to_lower_copy(filename.extension().string()); std::string extension = boost::to_lower_copy(filename.extension().string());
std::string expectedExtension; std::string expectedExtension;
@ -370,6 +344,8 @@ public:
Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str()); Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str());
ref<FileStream> stream = new FileStream(filename, FileStream::ETruncWrite); ref<FileStream> stream = new FileStream(filename, FileStream::ETruncWrite);
annotate(scene, m_properties, bitmap, renderTime, m_gamma);
bitmap->write(m_fileFormat, stream); bitmap->write(m_fileFormat, stream);
} }
@ -415,11 +391,6 @@ public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
protected: protected:
struct Annotation {
Point2i offset;
std::string text;
};
Bitmap::EFileFormat m_fileFormat; Bitmap::EFileFormat m_fileFormat;
Bitmap::EPixelFormat m_pixelFormat; Bitmap::EPixelFormat m_pixelFormat;
bool m_hasBanner; bool m_hasBanner;
@ -428,9 +399,6 @@ protected:
ref<ImageBlock> m_storage; ref<ImageBlock> m_storage;
ETonemapMethod m_tonemapMethod; ETonemapMethod m_tonemapMethod;
Float m_exposure, m_reinhardKey, m_reinhardBurn; Float m_exposure, m_reinhardKey, m_reinhardBurn;
std::vector<Annotation> m_annotations;
std::map<std::string, std::string> m_tags;
}; };
MTS_IMPLEMENT_CLASS_S(LDRFilm, false, Film) MTS_IMPLEMENT_CLASS_S(LDRFilm, false, Film)

View File

@ -238,7 +238,7 @@ public:
m_destFile = destFile; m_destFile = destFile;
} }
void develop() { void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film .."); Log(EDebug, "Developing film ..");
fs::path filename = m_destFile; fs::path filename = m_destFile;

View File

@ -156,7 +156,7 @@ public:
} }
virtual ~TiledHDRFilm() { virtual ~TiledHDRFilm() {
develop(); develop(NULL, 0);
} }
void serialize(Stream *stream, InstanceManager *manager) const { void serialize(Stream *stream, InstanceManager *manager) const {
@ -167,7 +167,7 @@ public:
void setDestinationFile(const fs::path &destFile, uint32_t blockSize) { void setDestinationFile(const fs::path &destFile, uint32_t blockSize) {
if (m_output) if (m_output)
develop(); develop(NULL, 0);
Bitmap::EPixelFormat pixelFormat = m_pixelFormat; Bitmap::EPixelFormat pixelFormat = m_pixelFormat;
#if SPECTRUM_SAMPLES == 3 #if SPECTRUM_SAMPLES == 3
@ -436,7 +436,7 @@ public:
return false; /* Not supported by the tiled EXR film! */ return false; /* Not supported by the tiled EXR film! */
} }
void develop() { void develop(const Scene *scene, Float renderTime) {
if (m_output) { if (m_output) {
Log(EInfo, "Closing EXR file (%u tiles in total, peak memory usage: %u tiles)..", Log(EInfo, "Closing EXR file (%u tiles in total, peak memory usage: %u tiles)..",
m_blocksH * m_blocksV, m_peakUsage); m_blocksH * m_blocksV, m_peakUsage);

View File

@ -9,7 +9,7 @@ bidirEnv.Append(CPPDEFINES = [['MTS_BUILD_MODULE', 'MTS_MODULE_BIDIR']])
libbidir = bidirEnv.SharedLibrary('mitsuba-bidir', [ libbidir = bidirEnv.SharedLibrary('mitsuba-bidir', [
'common.cpp', 'rsampler.cpp', 'vertex.cpp', 'edge.cpp', 'common.cpp', 'rsampler.cpp', 'vertex.cpp', 'edge.cpp',
'path.cpp', 'verification.cpp', 'util.cpp', 'pathsampler.cpp', 'path.cpp', 'verification.cpp', 'util.cpp', 'pathsampler.cpp',
'mut_bidir.cpp', 'mut_lens.cpp', 'mut_caustic.cpp', 'mut_bidir.cpp', 'mut_lens.cpp', 'mut_caustic.cpp',
'mut_mchain.cpp', 'manifold.cpp', 'mut_manifold.cpp' 'mut_mchain.cpp', 'manifold.cpp', 'mut_manifold.cpp'
]) ])

View File

@ -70,6 +70,7 @@ set(HDRS
${INCLUDE_DIR}/thread.h ${INCLUDE_DIR}/thread.h
${INCLUDE_DIR}/timer.h ${INCLUDE_DIR}/timer.h
${INCLUDE_DIR}/tls.h ${INCLUDE_DIR}/tls.h
${INCLUDE_DIR}/track.h
${INCLUDE_DIR}/transform.h ${INCLUDE_DIR}/transform.h
${INCLUDE_DIR}/triangle.h ${INCLUDE_DIR}/triangle.h
${INCLUDE_DIR}/util.h ${INCLUDE_DIR}/util.h
@ -118,6 +119,7 @@ set(SRCS
thread.cpp thread.cpp
timer.cpp timer.cpp
tls.cpp tls.cpp
track.cpp
transform.cpp transform.cpp
triangle.cpp triangle.cpp
util.cpp util.cpp

View File

@ -34,7 +34,7 @@ libcore_objects = [
'mstream.cpp', 'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp', 'mstream.cpp', 'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp',
'zstream.cpp', 'shvector.cpp', 'fresolver.cpp', 'rfilter.cpp', 'zstream.cpp', 'shvector.cpp', 'fresolver.cpp', 'rfilter.cpp',
'quad.cpp', 'mmap.cpp', 'chisquare.cpp', 'warp.cpp', 'vmf.cpp', 'quad.cpp', 'mmap.cpp', 'chisquare.cpp', 'warp.cpp', 'vmf.cpp',
'tls.cpp', 'ssemath.cpp', 'spline.cpp' 'tls.cpp', 'ssemath.cpp', 'spline.cpp', 'track.cpp'
] ]
# Add some platform-specific components # Add some platform-specific components

View File

@ -1355,7 +1355,7 @@ void Bitmap::writePNG(Stream *stream, int compression) const {
png_text *text = NULL; png_text *text = NULL;
Properties metadata(m_metadata); Properties metadata(m_metadata);
metadata.setString("generated-by", "Mitsuba version " MTS_VERSION); metadata.setString("generatedBy", "Mitsuba version " MTS_VERSION);
std::vector<std::string> keys = metadata.getPropertyNames(); std::vector<std::string> keys = metadata.getPropertyNames();
std::vector<std::string> values(keys.size()); std::vector<std::string> values(keys.size());
@ -1923,7 +1923,7 @@ void Bitmap::writeOpenEXR(Stream *stream,
#endif #endif
Properties metadata(m_metadata); Properties metadata(m_metadata);
metadata.setString("generated-by", "Mitsuba version " MTS_VERSION); metadata.setString("generatedBy", "Mitsuba version " MTS_VERSION);
std::vector<std::string> keys = metadata.getPropertyNames(); std::vector<std::string> keys = metadata.getPropertyNames();

View File

@ -156,7 +156,6 @@ template <typename T> struct FormatConverterImpl : public FormatConverter {
precomp[i] = convertScalar<DestFormat>(detail::safe_cast<SourceFormat>(i), sourceGamma, NULL, multiplier, invDestGamma); precomp[i] = convertScalar<DestFormat>(detail::safe_cast<SourceFormat>(i), sourceGamma, NULL, multiplier, invDestGamma);
} }
const DestFormat zero = convertScalar<DestFormat>(0.0f);
const DestFormat one = convertScalar<DestFormat>(1.0f); const DestFormat one = convertScalar<DestFormat>(1.0f);
Spectrum spec; Spectrum spec;
@ -193,14 +192,14 @@ template <typename T> struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZ: case Bitmap::EXYZ:
for (size_t i=0; i<count; ++i) { for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma); DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma);
*dest++ = zero; *dest++ = value; *dest++ = zero; *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
} }
break; break;
case Bitmap::EXYZA: case Bitmap::EXYZA:
for (size_t i=0; i<count; ++i) { for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma); DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma);
*dest++ = zero; *dest++ = value; *dest++ = zero; *dest++ = one; *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value; *dest++ = one;
} }
break; break;
@ -271,7 +270,7 @@ template <typename T> struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZ: case Bitmap::EXYZ:
for (size_t i=0; i<count; ++i) { for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma); DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma);
*dest++ = zero; *dest++ = value; *dest++ = zero; *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
source++; source++;
} }
break; break;
@ -279,7 +278,7 @@ template <typename T> struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZA: case Bitmap::EXYZA:
for (size_t i=0; i<count; ++i) { for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma); DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma);
*dest++ = zero; *dest++ = value; *dest++ = zero; *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
*dest++ = convertScalar<DestFormat>(*source++); *dest++ = convertScalar<DestFormat>(*source++);
} }
break; break;

View File

@ -18,6 +18,7 @@
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
#include <mitsuba/core/netobject.h> #include <mitsuba/core/netobject.h>
#include <mitsuba/core/track.h>
/* Keep the boost::variant includes outside of properties.h, /* Keep the boost::variant includes outside of properties.h,
since they noticeably add to the overall compile times */ since they noticeably add to the overall compile times */
@ -26,7 +27,7 @@
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
typedef boost::variant< typedef boost::variant<
bool, int64_t, Float, Point, Vector, Transform, bool, int64_t, Float, Point, Vector, Transform, AnimatedTransform *,
Spectrum, std::string, Properties::Data> ElementData; Spectrum, std::string, Properties::Data> ElementData;
struct PropertyElement { struct PropertyElement {
@ -78,33 +79,104 @@ DEFINE_PROPERTY_ACCESSOR(Spectrum, Spectrum, Spectrum, spectrum)
DEFINE_PROPERTY_ACCESSOR(std::string, std::string, String, string) DEFINE_PROPERTY_ACCESSOR(std::string, std::string, String, string)
DEFINE_PROPERTY_ACCESSOR(Properties::Data, Properties::Data, Data, data) DEFINE_PROPERTY_ACCESSOR(Properties::Data, Properties::Data, Data, data)
void Properties::setAnimatedTransform(const std::string &name, const AnimatedTransform *value, bool warnDuplicates) {
if (hasProperty(name)) {
AnimatedTransform **old = boost::get<AnimatedTransform *>(&((*m_elements)[name].data));
if (old)
(*old)->decRef();
if (warnDuplicates)
SLog(EWarn, "Property \"%s\" was specified multiple times!", name.c_str());
}
(*m_elements)[name].data = (AnimatedTransform *) value;
(*m_elements)[name].queried = false;
value->incRef();
}
ref<const AnimatedTransform> Properties::getAnimatedTransform(const std::string &name) const {
std::map<std::string, PropertyElement>::const_iterator it = m_elements->find(name);
if (it == m_elements->end())
SLog(EError, "Property \"%s\" missing", name.c_str());
const AnimatedTransform * const * result1 = boost::get<AnimatedTransform *>(&it->second.data);
const Transform *result2 = boost::get<Transform>(&it->second.data);
if (!result1 && !result2)
SLog(EError, "The property \"%s\" has the wrong type (expected <animation> or <transform>). The "
"complete property record is :\n%s", name.c_str(), toString().c_str());
it->second.queried = true;
if (result1)
return *result1;
else
return new AnimatedTransform(*result2);
}
ref<const AnimatedTransform> Properties::getAnimatedTransform(const std::string &name, const AnimatedTransform *defVal) const {
std::map<std::string, PropertyElement>::const_iterator it = m_elements->find(name);
if (it == m_elements->end())
return defVal;
AnimatedTransform * const * result1 = boost::get<AnimatedTransform *>(&it->second.data);
const Transform *result2 = boost::get<Transform>(&it->second.data);
if (!result1 && !result2)
SLog(EError, "The property \"%s\" has the wrong type (expected <animation> or <transform>). The "
"complete property record is :\n%s", name.c_str(), toString().c_str());
it->second.queried = true;
if (result1)
return *result1;
else
return new AnimatedTransform(*result2);
}
ref<const AnimatedTransform> Properties::getAnimatedTransform(const std::string &name, const Transform &defVal) const {
std::map<std::string, PropertyElement>::const_iterator it = m_elements->find(name);
if (it == m_elements->end())
return new AnimatedTransform(defVal);
AnimatedTransform * const * result1 = boost::get<AnimatedTransform *>(&it->second.data);
const Transform *result2 = boost::get<Transform>(&it->second.data);
if (!result1 && !result2)
SLog(EError, "The property \"%s\" has the wrong type (expected <animation> or <transform>). The "
"complete property record is :\n%s", name.c_str(), toString().c_str());
it->second.queried = true;
if (result1)
return *result1;
else
return new AnimatedTransform(*result2);
}
namespace { namespace {
class TypeVisitor : public boost::static_visitor<Properties::EPropertyType> { class TypeVisitor : public boost::static_visitor<Properties::EPropertyType> {
public: public:
Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; } Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; }
Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; } Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; }
Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; } Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; }
Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; } Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; }
Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; } Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; }
Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; } Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; }
Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; } Properties::EPropertyType operator()(const AnimatedTransform *) const { return Properties::EAnimatedTransform; }
Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; } Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; }
Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; } Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; }
Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; }
}; };
class EqualityVisitor : public boost::static_visitor<bool> { class EqualityVisitor : public boost::static_visitor<bool> {
public: public:
EqualityVisitor(const ElementData *ref) : ref(ref) { } EqualityVisitor(const ElementData *ref) : ref(ref) { }
bool operator()(const bool &v) const { const bool *v2 = boost::get<bool>(ref); return v2 ? (v == *v2) : false; } bool operator()(const bool &v) const { const bool *v2 = boost::get<bool>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const int64_t &v) const { const int64_t *v2 = boost::get<int64_t>(ref); return v2 ? (v == *v2) : false; } bool operator()(const int64_t &v) const { const int64_t *v2 = boost::get<int64_t>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Float &v) const { const Float *v2 = boost::get<Float>(ref); return v2 ? (v == *v2) : false; } bool operator()(const Float &v) const { const Float *v2 = boost::get<Float>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Point &v) const { const Point *v2 = boost::get<Point>(ref); return v2 ? (v == *v2) : false; } bool operator()(const Point &v) const { const Point *v2 = boost::get<Point>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Vector &v) const { const Vector *v2 = boost::get<Vector>(ref); return v2 ? (v == *v2) : false; } bool operator()(const Vector &v) const { const Vector *v2 = boost::get<Vector>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Transform &v) const { const Transform *v2 = boost::get<Transform>(ref); return v2 ? (v == *v2) : false; } bool operator()(const Transform &v) const { const Transform *v2 = boost::get<Transform>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Spectrum &v) const { const Spectrum *v2 = boost::get<Spectrum>(ref); return v2 ? (v == *v2) : false; } bool operator()(const AnimatedTransform *v) const { AnimatedTransform * const *v2 = boost::get<AnimatedTransform*>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const std::string &v) const { const std::string *v2 = boost::get<std::string>(ref); return v2 ? (v == *v2) : false; } bool operator()(const Spectrum &v) const { const Spectrum *v2 = boost::get<Spectrum>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Properties::Data &v) const { const Properties::Data *v2 = boost::get<Properties::Data>(ref); return v2 ? (v == *v2) : false; } bool operator()(const std::string &v) const { const std::string *v2 = boost::get<std::string>(ref); return v2 ? (v == *v2) : false; }
bool operator()(const Properties::Data &v) const { const Properties::Data *v2 = boost::get<Properties::Data>(ref); return v2 ? (v == *v2) : false; }
private: private:
const ElementData *ref; const ElementData *ref;
}; };
@ -113,15 +185,16 @@ namespace {
public: public:
StringVisitor(std::ostringstream &oss, bool quote) : oss(oss), quote(quote) { } StringVisitor(std::ostringstream &oss, bool quote) : oss(oss), quote(quote) { }
void operator()(const bool &v) const { oss << (v ? "true" : "false"); } void operator()(const bool &v) const { oss << (v ? "true" : "false"); }
void operator()(const int64_t &v) const { oss << v; } void operator()(const int64_t &v) const { oss << v; }
void operator()(const Float &v) const { oss << v; } void operator()(const Float &v) const { oss << v; }
void operator()(const Point &v) const { oss << v.toString(); } void operator()(const Point &v) const { oss << v.toString(); }
void operator()(const Vector &v) const { oss << v.toString(); } void operator()(const Vector &v) const { oss << v.toString(); }
void operator()(const Transform &v) const { oss << v.toString(); } void operator()(const Transform &v) const { oss << v.toString(); }
void operator()(const Spectrum &v) const { oss << v.toString(); } void operator()(const AnimatedTransform *v) const { oss << ((Object *) v)->toString(); }
void operator()(const std::string &v) const { oss << (quote ? "\"" : "") << v << (quote ? "\"" : ""); } void operator()(const Spectrum &v) const { oss << v.toString(); }
void operator()(const Properties::Data &v) const { oss << v.ptr << " (size=" << v.size << ")"; } void operator()(const std::string &v) const { oss << (quote ? "\"" : "") << v << (quote ? "\"" : ""); }
void operator()(const Properties::Data &v) const { oss << v.ptr << " (size=" << v.size << ")"; }
private: private:
std::ostringstream &oss; std::ostringstream &oss;
bool quote; bool quote;
@ -141,16 +214,44 @@ Properties::Properties(const std::string &pluginName)
Properties::Properties(const Properties &props) Properties::Properties(const Properties &props)
: m_pluginName(props.m_pluginName), m_id(props.m_id) { : m_pluginName(props.m_pluginName), m_id(props.m_id) {
m_elements = new std::map<std::string, PropertyElement>(*props.m_elements); m_elements = new std::map<std::string, PropertyElement>(*props.m_elements);
for (std::map<std::string, PropertyElement>::iterator it = m_elements->begin();
it != m_elements->end(); ++it) {
AnimatedTransform **trafo = boost::get<AnimatedTransform *>(&(*it).second.data);
if (trafo)
(*trafo)->incRef();
}
} }
Properties::~Properties() { Properties::~Properties() {
for (std::map<std::string, PropertyElement>::iterator it = m_elements->begin();
it != m_elements->end(); ++it) {
AnimatedTransform **trafo = boost::get<AnimatedTransform *>(&(*it).second.data);
if (trafo)
(*trafo)->decRef();
}
delete m_elements; delete m_elements;
} }
void Properties::operator=(const Properties &props) { void Properties::operator=(const Properties &props) {
for (std::map<std::string, PropertyElement>::iterator it = m_elements->begin();
it != m_elements->end(); ++it) {
AnimatedTransform **trafo = boost::get<AnimatedTransform *>(&(*it).second.data);
if (trafo)
(*trafo)->decRef();
}
m_pluginName = props.m_pluginName; m_pluginName = props.m_pluginName;
m_id = props.m_id; m_id = props.m_id;
*m_elements = *props.m_elements; *m_elements = *props.m_elements;
for (std::map<std::string, PropertyElement>::iterator it = m_elements->begin();
it != m_elements->end(); ++it) {
AnimatedTransform **trafo = boost::get<AnimatedTransform *>(&(*it).second.data);
if (trafo)
(*trafo)->incRef();
}
} }
bool Properties::hasProperty(const std::string &name) const { bool Properties::hasProperty(const std::string &name) const {
@ -161,6 +262,9 @@ bool Properties::removeProperty(const std::string &name) {
std::map<std::string, PropertyElement>::iterator it = m_elements->find(name); std::map<std::string, PropertyElement>::iterator it = m_elements->find(name);
if (it == m_elements->end()) if (it == m_elements->end())
return false; return false;
AnimatedTransform **trafo = boost::get<AnimatedTransform *>(&(*it).second.data);
if (trafo)
(*trafo)->decRef();
m_elements->erase(it); m_elements->erase(it);
return true; return true;
} }
@ -246,6 +350,14 @@ void Properties::putPropertyNames(std::vector<std::string> &results) const {
results.push_back((*it).first); results.push_back((*it).first);
} }
void Properties::copyAttribute(const Properties &properties,
const std::string &sourceName, const std::string &targetName) {
std::map<std::string, PropertyElement>::const_iterator it = properties.m_elements->find(sourceName);
if (it == properties.m_elements->end())
SLog(EError, "copyAttribute(): Could not find parameter \"%s\"!", sourceName.c_str());
m_elements->operator[](targetName) = it->second;
}
bool Properties::operator==(const Properties &p) const { bool Properties::operator==(const Properties &p) const {
if (m_pluginName != p.m_pluginName || m_id != p.m_id || m_elements->size() != p.m_elements->size()) if (m_pluginName != p.m_pluginName || m_id != p.m_id || m_elements->size() != p.m_elements->size())
return false; return false;

View File

@ -1,8 +1,18 @@
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/aabb.h> #include <mitsuba/core/aabb.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
AnimatedTransform::AnimatedTransform(const AnimatedTransform *trafo)
: m_transform(trafo->m_transform) {
m_tracks.reserve(trafo->getTrackCount());
for (size_t i=0; i<trafo->getTrackCount(); ++i) {
AbstractAnimationTrack *track = trafo->getTrack(i)->clone();
m_tracks.push_back(track);
track->incRef();
}
}
AnimatedTransform::AnimatedTransform(Stream *stream) { AnimatedTransform::AnimatedTransform(Stream *stream) {
size_t nTracks = stream->readSize(); size_t nTracks = stream->readSize();
if (nTracks == 0) { if (nTracks == 0) {
@ -48,18 +58,9 @@ void AnimatedTransform::addTrack(AbstractAnimationTrack *track) {
AABB1 AnimatedTransform::getTimeBounds() const { AABB1 AnimatedTransform::getTimeBounds() const {
if (m_tracks.size() == 0) if (m_tracks.size() == 0)
#if !defined(__clang__)
return AABB1(0.0f, 0.0f); return AABB1(0.0f, 0.0f);
#else
// HACK Workaround for clang
{
AABB1 b;
b.min = b.max = 0.0f;
return b;
}
#endif
Float min = std::numeric_limits<Float>::infinity(); Float min = std::numeric_limits<Float>::infinity();
Float max = -std::numeric_limits<Float>::infinity(); Float max = -std::numeric_limits<Float>::infinity();
for (size_t i=0; i<m_tracks.size(); ++i) { for (size_t i=0; i<m_tracks.size(); ++i) {
@ -70,15 +71,7 @@ AABB1 AnimatedTransform::getTimeBounds() const {
max = std::max(max, track->getTime(size-1)); max = std::max(max, track->getTime(size-1));
} }
#if !defined(__clang__)
return AABB1(min, max); return AABB1(min, max);
#else
// HACK Workaround for clang
AABB1 b;
b.min = min;
b.max = max;
return b;
#endif
} }
AABB AnimatedTransform::getTranslationBounds() const { AABB AnimatedTransform::getTranslationBounds() const {
@ -152,6 +145,122 @@ AnimatedTransform::~AnimatedTransform() {
m_tracks[i]->decRef(); m_tracks[i]->decRef();
} }
void AnimatedTransform::sortAndSimplify() {
bool isStatic = true;
for (size_t i=0; i<m_tracks.size(); ++i) {
AbstractAnimationTrack *track = m_tracks[i];
bool isNeeded = false;
switch (track->getType()) {
case AbstractAnimationTrack::ETranslationX:
case AbstractAnimationTrack::ETranslationY:
case AbstractAnimationTrack::ETranslationZ:
case AbstractAnimationTrack::ERotationX:
case AbstractAnimationTrack::ERotationY:
case AbstractAnimationTrack::ERotationZ:
case AbstractAnimationTrack::EScaleX:
case AbstractAnimationTrack::EScaleY:
case AbstractAnimationTrack::EScaleZ:
isNeeded = static_cast<FloatTrack *>(track)->sortAndSimplify();
break;
case AbstractAnimationTrack::ETranslationXYZ:
case AbstractAnimationTrack::EScaleXYZ:
isNeeded = static_cast<VectorTrack *>(track)->sortAndSimplify();
break;
case AbstractAnimationTrack::ERotationQuat:
isNeeded = static_cast<QuatTrack *>(track)->sortAndSimplify();
break;
default:
Log(EError, "Encountered an unsupported "
"animation track type: %i!", track->getType());
}
if (isNeeded) {
isStatic &= track->getSize() == 1;
} else {
m_tracks.erase(m_tracks.begin() + i);
track->decRef();
--i;
}
}
if (isStatic) {
Transform temp;
temp = eval(0);
m_transform = temp;
for (size_t i=0; i<m_tracks.size(); ++i)
m_tracks[i]->decRef();
m_tracks.clear();
}
}
const AbstractAnimationTrack *AnimatedTransform::findTrack(AbstractAnimationTrack::EType type) const {
for (size_t i=0; i<m_tracks.size(); ++i) {
AbstractAnimationTrack *track = m_tracks[i];
if (track->getType() == type)
return track;
}
return NULL;
}
AbstractAnimationTrack *AnimatedTransform::findTrack(AbstractAnimationTrack::EType type) {
for (size_t i=0; i<m_tracks.size(); ++i) {
AbstractAnimationTrack *track = m_tracks[i];
if (track->getType() == type)
return track;
}
return NULL;
}
void AnimatedTransform::prependScale(const Vector &scale) {
FloatTrack *trackX = (FloatTrack *) findTrack(AbstractAnimationTrack::EScaleX);
FloatTrack *trackY = (FloatTrack *) findTrack(AbstractAnimationTrack::EScaleY);
FloatTrack *trackZ = (FloatTrack *) findTrack(AbstractAnimationTrack::EScaleZ);
VectorTrack *trackXYZ = (VectorTrack *) findTrack(AbstractAnimationTrack::EScaleXYZ);
if (m_tracks.empty()) {
m_transform = m_transform * Transform::scale(scale);
} else if (trackXYZ) {
trackXYZ->prependTransformation(scale);
} else if (trackX && trackY && trackZ) {
if (trackX) {
trackX->prependTransformation(scale.x);
} else {
trackX = new FloatTrack(AbstractAnimationTrack::EScaleX);
trackX->append(0.0f, scale.x); addTrack(trackX);
}
if (trackY) {
trackY->prependTransformation(scale.y);
} else {
trackY = new FloatTrack(AbstractAnimationTrack::EScaleY);
trackY->append(0.0f, scale.y); addTrack(trackY);
}
if (trackZ) {
trackZ->prependTransformation(scale.z);
} else {
trackZ = new FloatTrack(AbstractAnimationTrack::EScaleZ);
trackZ->append(0.0f, scale.z); addTrack(trackZ);
}
} else {
trackXYZ = new VectorTrack(AbstractAnimationTrack::EScaleXYZ);
trackXYZ->append(0.0f, scale);
addTrack(trackXYZ);
}
}
void AnimatedTransform::collectKeyframes(std::set<Float> &result) const {
for (size_t i=0; i<m_tracks.size(); ++i) {
const AbstractAnimationTrack *track = m_tracks[i];
for (size_t j=0; j<track->getSize(); ++j)
result.insert(track->getTime(j));
}
if (result.size() == 0)
result.insert(0);
}
void AnimatedTransform::serialize(Stream *stream) const { void AnimatedTransform::serialize(Stream *stream) const {
stream->writeSize(m_tracks.size()); stream->writeSize(m_tracks.size());
if (m_tracks.size() == 0) { if (m_tracks.size() == 0) {
@ -203,9 +312,13 @@ void AnimatedTransform::TransformFunctor::operator()(const Float &t, Transform &
} }
} }
trafo = Transform::translate(translation) * trafo = Transform::translate(translation);
rotation.toTransform() *
Transform::scale(scale); if (!rotation.isIdentity())
trafo = trafo * rotation.toTransform();
if (scale != Vector(0.0f))
trafo = trafo * Transform::scale(scale);
} }
std::string AnimatedTransform::toString() const { std::string AnimatedTransform::toString() const {

View File

@ -28,13 +28,15 @@
#if defined(__OSX__) #if defined(__OSX__)
#include <sys/sysctl.h> #include <sys/sysctl.h>
#elif defined(WIN32) #include <mach/mach.h>
#elif defined(__WINDOWS__)
#include <direct.h> #include <direct.h>
#include <psapi.h>
#else #else
#include <malloc.h> #include <malloc.h>
#endif #endif
#if defined(WIN32) #if defined(__WINDOWS__)
# include <windows.h> # include <windows.h>
# include <winsock2.h> # include <winsock2.h>
# include <ws2tcpip.h> # include <ws2tcpip.h>
@ -134,7 +136,7 @@ std::string memString(size_t size) {
} }
void * __restrict allocAligned(size_t size) { void * __restrict allocAligned(size_t size) {
#if defined(WIN32) #if defined(__WINDOWS__)
return _aligned_malloc(size, L1_CACHE_LINE_SIZE); return _aligned_malloc(size, L1_CACHE_LINE_SIZE);
#elif defined(__OSX__) #elif defined(__OSX__)
/* OSX malloc already returns 16-byte aligned data suitable /* OSX malloc already returns 16-byte aligned data suitable
@ -146,7 +148,7 @@ void * __restrict allocAligned(size_t size) {
} }
void freeAligned(void *ptr) { void freeAligned(void *ptr) {
#if defined(WIN32) #if defined(__WINDOWS__)
_aligned_free(ptr); _aligned_free(ptr);
#else #else
free(ptr); free(ptr);
@ -154,7 +156,7 @@ void freeAligned(void *ptr) {
} }
int getCoreCount() { int getCoreCount() {
#if defined(WIN32) #if defined(__WINDOWS__)
SYSTEM_INFO sys_info; SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info); GetSystemInfo(&sys_info);
return sys_info.dwNumberOfProcessors; return sys_info.dwNumberOfProcessors;
@ -169,7 +171,45 @@ int getCoreCount() {
#endif #endif
} }
#if defined(WIN32) size_t getPrivateMemoryUsage() {
#if defined(__WINDOWS__)
PROCESS_MEMORY_COUNTERS_EX pmc;
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
return (size_t) pmc.PrivateUsage; /* Process-private memory usage (RAM + swap) */
#elif defined(__OSX__)
struct task_basic_info_64 t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_64_COUNT;
if (task_info(mach_task_self(), TASK_BASIC_INFO_64,
(task_info_t)&t_info, &t_info_count) != KERN_SUCCESS)
return 0;
return (size_t) t_info.resident_size; /* Not exactly what we want -- oh well.. */
#else
FILE* file = fopen("/proc/self/status", "r");
if (!file)
return 0;
char buffer[128];
size_t result = 0;
while (fgets(buffer, sizeof(buffer), file) != NULL) {
if (strncmp(buffer, "VmRSS:", 6) != 0 && /* Non-swapped physical memory specific to this process */
strncmp(buffer, "VmSwap:", 7) != 0) /* Swapped memory specific to this process */
continue;
char *line = buffer;
while (*line < '0' || *line > '9')
++line;
line[strlen(line)-3] = '\0';
result += (size_t) atoi(line) * 1024;
}
fclose(file);
return result;
#endif
}
#if defined(__WINDOWS__)
std::string lastErrorText() { std::string lastErrorText() {
DWORD errCode = GetLastError(); DWORD errCode = GetLastError();
char *errorText = NULL; char *errorText = NULL;
@ -192,7 +232,7 @@ std::string lastErrorText() {
bool enableFPExceptions() { bool enableFPExceptions() {
bool exceptionsWereEnabled = false; bool exceptionsWereEnabled = false;
#if defined(WIN32) #if defined(__WINDOWS__)
_clearfp(); _clearfp();
uint32_t cw = _controlfp(0, 0); uint32_t cw = _controlfp(0, 0);
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
@ -211,7 +251,7 @@ bool enableFPExceptions() {
bool disableFPExceptions() { bool disableFPExceptions() {
bool exceptionsWereEnabled = false; bool exceptionsWereEnabled = false;
#if defined(WIN32) #if defined(__WINDOWS__)
_clearfp(); _clearfp();
uint32_t cw = _controlfp(0, 0); uint32_t cw = _controlfp(0, 0);
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
@ -230,7 +270,7 @@ bool disableFPExceptions() {
void restoreFPExceptions(bool oldState) { void restoreFPExceptions(bool oldState) {
bool currentState; bool currentState;
#if defined(WIN32) #if defined(__WINDOWS__)
uint32_t cw = _controlfp(0, 0); uint32_t cw = _controlfp(0, 0);
currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
#elif defined(__OSX__) #elif defined(__OSX__)
@ -249,7 +289,7 @@ void restoreFPExceptions(bool oldState) {
std::string getHostName() { std::string getHostName() {
char hostName[128]; char hostName[128];
if (gethostname(hostName, sizeof(hostName)) != 0) if (gethostname(hostName, sizeof(hostName)) != 0)
#if defined(WIN32) #if defined(__WINDOWS__)
SLog(EError, "Could not retrieve the computer's host name: %s!", SLog(EError, "Could not retrieve the computer's host name: %s!",
lastErrorText().c_str()); lastErrorText().c_str());
#else #else
@ -280,7 +320,7 @@ std::string getFQDN() {
fqdn, NI_MAXHOST, NULL, 0, 0); fqdn, NI_MAXHOST, NULL, 0, 0);
if (retVal != 0) { if (retVal != 0) {
freeaddrinfo(addrInfo); freeaddrinfo(addrInfo);
#if defined(WIN32) #if defined(__WINDOWS__)
SLog(EWarn, "Could not retrieve the computer's fully " SLog(EWarn, "Could not retrieve the computer's fully "
"qualified domain name: error %i!", WSAGetLastError()); "qualified domain name: error %i!", WSAGetLastError());
#else #else
@ -304,7 +344,7 @@ std::string formatString(const char *fmt, ...) {
char tmp[512]; char tmp[512];
va_list iterator; va_list iterator;
#if defined(WIN32) #if defined(__WINDOWS__)
va_start(iterator, fmt); va_start(iterator, fmt);
size_t size = _vscprintf(fmt, iterator) + 1; size_t size = _vscprintf(fmt, iterator) + 1;

View File

@ -1,8 +1,8 @@
Import('env', 'sys', 'os') Import('env', 'sys', 'os')
libhw_objects = [ libhw_objects = [
'session.cpp', 'device.cpp', 'gputexture.cpp', 'gpugeometry.cpp', 'session.cpp', 'device.cpp', 'gputexture.cpp', 'gpugeometry.cpp',
'gpuprogram.cpp', 'renderer.cpp', 'glrenderer.cpp', 'glprogram.cpp', 'gpuprogram.cpp', 'renderer.cpp', 'glrenderer.cpp', 'glprogram.cpp',
'glgeometry.cpp', 'gltexture.cpp', 'gpusync.cpp', 'glsync.cpp', 'glgeometry.cpp', 'gltexture.cpp', 'gpusync.cpp', 'glsync.cpp',
'vpl.cpp', 'font.cpp', 'viewer.cpp', 'basicshader.cpp', 'shadow.cpp'] 'vpl.cpp', 'font.cpp', 'viewer.cpp', 'basicshader.cpp', 'shadow.cpp']
@ -14,7 +14,7 @@ if sys.platform == 'win32':
elif sys.platform == 'linux2': elif sys.platform == 'linux2':
libhw_objects += ['x11session.cpp', libhw_objects += ['x11session.cpp',
'x11device.cpp', 'x11device.cpp',
'glxdevice.cpp', 'glxdevice.cpp',
'glxrenderer.cpp'] 'glxrenderer.cpp']
glEnv = env.Clone() glEnv = env.Clone()

View File

@ -146,6 +146,7 @@ void VPLShaderManager::setScene(const Scene *scene) {
m_geometry.reserve(shapes.size()); m_geometry.reserve(shapes.size());
m_opaqueGeometry.clear(); m_opaqueGeometry.clear();
m_opaqueGeometry.reserve(shapes.size()); m_opaqueGeometry.reserve(shapes.size());
m_animatedGeometry.clear();
Matrix4x4 identityTrafo; Matrix4x4 identityTrafo;
identityTrafo.setIdentity(); identityTrafo.setIdentity();
@ -158,7 +159,8 @@ void VPLShaderManager::setScene(const Scene *scene) {
const Instance *instance = static_cast<const Instance *>(shape); const Instance *instance = static_cast<const Instance *>(shape);
const std::vector<const Shape *> &instantiatedShapes = const std::vector<const Shape *> &instantiatedShapes =
instance->getShapeGroup()->getKDTree()->getShapes(); instance->getShapeGroup()->getKDTree()->getShapes();
const Matrix4x4 &trafo = instance->getWorldTransform().getMatrix(); const AnimatedTransform *atrafo = instance->getWorldTransform();
const Matrix4x4 &trafo = atrafo->eval(0).getMatrix();
for (size_t j=0; j<instantiatedShapes.size(); ++j) { for (size_t j=0; j<instantiatedShapes.size(); ++j) {
shape = instantiatedShapes[j]; shape = instantiatedShapes[j];
@ -173,10 +175,18 @@ void VPLShaderManager::setScene(const Scene *scene) {
} }
gpuGeo->setShader(shader); gpuGeo->setShader(shader);
ssize_t geometryIndex = (ssize_t) m_geometry.size(), opaqueGeometryIndex = -1;
m_geometry.push_back(std::make_pair(gpuGeo, trafo)); m_geometry.push_back(std::make_pair(gpuGeo, trafo));
if (shader && !(shader->getFlags() & Shader::ETransparent)) if (shader && !(shader->getFlags() & Shader::ETransparent)) {
opaqueGeometryIndex = (ssize_t) m_opaqueGeometry.size();
m_opaqueGeometry.push_back(std::make_pair(gpuGeo, trafo)); m_opaqueGeometry.push_back(std::make_pair(gpuGeo, trafo));
}
if (!atrafo->isStatic()) {
m_animatedGeometry.push_back(AnimatedGeometryRecord(atrafo,
geometryIndex, opaqueGeometryIndex));
}
} }
} else { } else {
GPUGeometry *gpuGeo = m_renderer->registerGeometry(shape); GPUGeometry *gpuGeo = m_renderer->registerGeometry(shape);
@ -233,8 +243,40 @@ void VPLShaderManager::setScene(const Scene *scene) {
m_backgroundParam_emitterScale = prog->getParameterID("emitterScale", false); m_backgroundParam_emitterScale = prog->getParameterID("emitterScale", false);
} }
std::vector<size_t> geometryPermutation(m_geometry.size()),
opaqueGeometryPermutation(m_opaqueGeometry.size());
for (size_t i=0; i<m_geometry.size(); ++i)
geometryPermutation[i] = i;
for (size_t i=0; i<m_opaqueGeometry.size(); ++i)
opaqueGeometryPermutation[i] = i;
/// Sort using the MaterialOrder to reduce material changes/pipeline flushes /// Sort using the MaterialOrder to reduce material changes/pipeline flushes
std::sort(m_geometry.begin(), m_geometry.end(), MaterialOrder()); std::sort(geometryPermutation.begin(),
geometryPermutation.end(), MaterialOrder(m_geometry));
std::sort(opaqueGeometryPermutation.begin(),
opaqueGeometryPermutation.end(), MaterialOrder(m_opaqueGeometry));
if (!m_animatedGeometry.empty()) {
std::vector<size_t> geometryPermutationInv(m_geometry.size()),
opaqueGeometryPermutationInv(m_opaqueGeometry.size());
for (size_t i=0; i<m_geometry.size(); ++i)
geometryPermutationInv[geometryPermutation[i]] = i;
for (size_t i=0; i<m_opaqueGeometry.size(); ++i)
opaqueGeometryPermutationInv[opaqueGeometryPermutation[i]] = i;
for (size_t i=0; i<m_animatedGeometry.size(); ++i) {
AnimatedGeometryRecord &agRec = m_animatedGeometry[i];
if (agRec.geometryIndex >= 0)
agRec.geometryIndex = geometryPermutationInv[agRec.geometryIndex];
if (agRec.opaqueGeometryIndex >= 0)
agRec.opaqueGeometryIndex = opaqueGeometryPermutationInv[agRec.opaqueGeometryIndex];
}
}
permute_inplace(&m_geometry[0], geometryPermutation);
permute_inplace(&m_opaqueGeometry[0], opaqueGeometryPermutation);
} }
void VPLShaderManager::setVPL(const VPL &vpl) { void VPLShaderManager::setVPL(const VPL &vpl) {
@ -244,6 +286,18 @@ void VPLShaderManager::setVPL(const VPL &vpl) {
m_nearClip = std::numeric_limits<Float>::infinity(); m_nearClip = std::numeric_limits<Float>::infinity();
m_farClip = -std::numeric_limits<Float>::infinity(); m_farClip = -std::numeric_limits<Float>::infinity();
/* Update animations */
for (size_t i=0; i<m_animatedGeometry.size(); ++i) {
const AnimatedGeometryRecord &agRec = m_animatedGeometry[i];
const Matrix4x4 &matrix = agRec.trafo->eval(vpl.its.time).getMatrix();
if (agRec.geometryIndex >= 0)
m_geometry[agRec.geometryIndex].second = matrix;
if (agRec.opaqueGeometryIndex >= 0)
m_opaqueGeometry[agRec.opaqueGeometryIndex].second = matrix;
}
if (vpl.type != EDirectionalEmitterVPL) { if (vpl.type != EDirectionalEmitterVPL) {
/* Trace a few rays from the VPL to estimate a suitable depth range */ /* Trace a few rays from the VPL to estimate a suitable depth range */
for (size_t i=0; i<sampleCount; ++i) { for (size_t i=0; i<sampleCount; ++i) {

View File

@ -42,7 +42,6 @@ set(HDRS
${INCLUDE_DIR}/subsurface.h ${INCLUDE_DIR}/subsurface.h
${INCLUDE_DIR}/testcase.h ${INCLUDE_DIR}/testcase.h
${INCLUDE_DIR}/texture.h ${INCLUDE_DIR}/texture.h
${INCLUDE_DIR}/track.h
${INCLUDE_DIR}/triaccel.h ${INCLUDE_DIR}/triaccel.h
${INCLUDE_DIR}/triaccel_sse.h ${INCLUDE_DIR}/triaccel_sse.h
${INCLUDE_DIR}/trimesh.h ${INCLUDE_DIR}/trimesh.h
@ -82,7 +81,6 @@ set(SRCS
subsurface.cpp subsurface.cpp
testcase.cpp testcase.cpp
texture.cpp texture.cpp
track.cpp
trimesh.cpp trimesh.cpp
util.cpp util.cpp
volume.cpp volume.cpp

View File

@ -11,13 +11,13 @@ if renderEnv.has_key('XERCESLIB'):
librender = renderEnv.SharedLibrary('mitsuba-render', [ librender = renderEnv.SharedLibrary('mitsuba-render', [
'bsdf.cpp', 'film.cpp', 'integrator.cpp', 'emitter.cpp', 'sensor.cpp', 'bsdf.cpp', 'film.cpp', 'integrator.cpp', 'emitter.cpp', 'sensor.cpp',
'skdtree.cpp', 'medium.cpp', 'renderjob.cpp', 'imageproc.cpp', 'skdtree.cpp', 'medium.cpp', 'renderjob.cpp', 'imageproc.cpp',
'rectwu.cpp', 'renderproc.cpp', 'imageblock.cpp', 'particleproc.cpp', 'rectwu.cpp', 'renderproc.cpp', 'imageblock.cpp', 'particleproc.cpp',
'renderqueue.cpp', 'scene.cpp', 'subsurface.cpp', 'texture.cpp', 'renderqueue.cpp', 'scene.cpp', 'subsurface.cpp', 'texture.cpp',
'shape.cpp', 'trimesh.cpp', 'sampler.cpp', 'util.cpp', 'irrcache.cpp', 'shape.cpp', 'trimesh.cpp', 'sampler.cpp', 'util.cpp', 'irrcache.cpp',
'testcase.cpp', 'photonmap.cpp', 'gatherproc.cpp', 'volume.cpp', 'testcase.cpp', 'photonmap.cpp', 'gatherproc.cpp', 'volume.cpp',
'vpl.cpp', 'shader.cpp', 'scenehandler.cpp', 'intersection.cpp', 'vpl.cpp', 'shader.cpp', 'scenehandler.cpp', 'intersection.cpp',
'track.cpp', 'common.cpp', 'phase.cpp', 'noise.cpp', 'photon.cpp' 'common.cpp', 'phase.cpp', 'noise.cpp', 'photon.cpp'
]) ])
if sys.platform == "darwin": if sys.platform == "darwin":

View File

@ -18,15 +18,14 @@
#include <mitsuba/render/emitter.h> #include <mitsuba/render/emitter.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/render/shape.h> #include <mitsuba/render/shape.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
AbstractEmitter::AbstractEmitter(const Properties &props) AbstractEmitter::AbstractEmitter(const Properties &props)
: ConfigurableObject(props), m_shape(NULL), m_type(0) { : ConfigurableObject(props), m_shape(NULL), m_type(0) {
m_worldTransform = new AnimatedTransform( m_worldTransform = props.getAnimatedTransform("toWorld", Transform());
props.getTransform("toWorld", Transform()));
} }
AbstractEmitter::AbstractEmitter(Stream *stream, InstanceManager *manager) AbstractEmitter::AbstractEmitter(Stream *stream, InstanceManager *manager)

View File

@ -54,20 +54,28 @@ void RenderQueue::unregisterListener(RenderListener *listener) {
listener->decRef(); listener->decRef();
} }
Float RenderQueue::getRenderTime(const RenderJob *job) const {
LockGuard lock(m_mutex);
std::map<RenderJob *, JobRecord>::const_iterator it = m_jobs.find(const_cast<RenderJob*>(job));
if (it == m_jobs.end())
Log(EError, "RenderQueue::getRenderJob() - job not found!");
unsigned int ms = m_timer->getMilliseconds() - it->second.startTime;
return ms / 1000.0f;
}
void RenderQueue::flush() { void RenderQueue::flush() {
LockGuard lock(m_mutex); LockGuard lock(m_mutex);
std::map<RenderJob *, JobRecord>::iterator it = m_jobs.begin(); std::map<RenderJob *, JobRecord>::iterator it = m_jobs.begin();
for (; it != m_jobs.end(); ++it) { for (; it != m_jobs.end(); ++it)
(*it).first->flush(); (*it).first->flush();
}
} }
void RenderQueue::removeJob(RenderJob *job, bool cancelled) { void RenderQueue::removeJob(RenderJob *job, bool cancelled) {
LockGuard lock(m_mutex); LockGuard lock(m_mutex);
std::map<RenderJob *, JobRecord>::iterator it = m_jobs.find(job); std::map<RenderJob *, JobRecord>::iterator it = m_jobs.find(job);
if (it == m_jobs.end()) { if (it == m_jobs.end())
Log(EError, "RenderQueue::removeRenderJob() - job not found!"); Log(EError, "RenderQueue::removeRenderJob() - job not found!");
}
JobRecord &rec = (*it).second; JobRecord &rec = (*it).second;
unsigned int ms = m_timer->getMilliseconds() - rec.startTime; unsigned int ms = m_timer->getMilliseconds() - rec.startTime;
Log(EInfo, "Render time: %s", timeString(ms/1000.0f, true).c_str()); Log(EInfo, "Render time: %s", timeString(ms/1000.0f, true).c_str());

View File

@ -451,8 +451,8 @@ void Scene::cancel() {
m_integrator->cancel(); m_integrator->cancel();
} }
void Scene::flush() { void Scene::flush(RenderQueue *queue, const RenderJob *job) {
m_sensor->getFilm()->develop(); m_sensor->getFilm()->develop(this, queue->getRenderTime(job));
} }
void Scene::setDestinationFile(const fs::path &name) { void Scene::setDestinationFile(const fs::path &name) {
@ -467,7 +467,7 @@ void Scene::postprocess(RenderQueue *queue, const RenderJob *job,
int sceneResID, int sensorResID, int samplerResID) { int sceneResID, int sensorResID, int samplerResID) {
m_integrator->postprocess(this, queue, job, sceneResID, m_integrator->postprocess(this, queue, job, sceneResID,
sensorResID, samplerResID); sensorResID, samplerResID);
m_sensor->getFilm()->develop(); m_sensor->getFilm()->develop(this, queue->getRenderTime(job));
} }
void Scene::addChild(const std::string &name, ConfigurableObject *child) { void Scene::addChild(const std::string &name, ConfigurableObject *child) {

View File

@ -31,6 +31,7 @@
#include <mitsuba/core/fresolver.h> #include <mitsuba/core/fresolver.h>
#include <mitsuba/render/scene.h> #include <mitsuba/render/scene.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <Eigen/SVD>
#include <boost/unordered_set.hpp> #include <boost/unordered_set.hpp>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -102,6 +103,7 @@ SceneHandler::SceneHandler(const SAXParser *parser,
m_tags["blackbody"] = TagEntry(EBlackBody, (Class *) NULL); m_tags["blackbody"] = TagEntry(EBlackBody, (Class *) NULL);
m_tags["spectrum"] = TagEntry(ESpectrum, (Class *) NULL); m_tags["spectrum"] = TagEntry(ESpectrum, (Class *) NULL);
m_tags["transform"] = TagEntry(ETransform, (Class *) NULL); m_tags["transform"] = TagEntry(ETransform, (Class *) NULL);
m_tags["animation"] = TagEntry(EAnimation, (Class *) NULL);
m_tags["include"] = TagEntry(EInclude, (Class *) NULL); m_tags["include"] = TagEntry(EInclude, (Class *) NULL);
m_tags["alias"] = TagEntry(EAlias, (Class *) NULL); m_tags["alias"] = TagEntry(EAlias, (Class *) NULL);
@ -179,11 +181,12 @@ void SceneHandler::characters(const XMLCh* const name,
Float SceneHandler::parseFloat(const std::string &name, Float SceneHandler::parseFloat(const std::string &name,
const std::string &str, Float defVal) const { const std::string &str, Float defVal) const {
char *end_ptr = NULL; char *end_ptr = NULL;
if (str == "") { if (str.empty()) {
if (defVal == -1) if (defVal == -1)
XMLLog(EError, "Missing floating point value (in <%s>)", name.c_str()); XMLLog(EError, "Missing floating point value (in <%s>)", name.c_str());
return defVal; return defVal;
} }
Float result = (Float) std::strtod(str.c_str(), &end_ptr); Float result = (Float) std::strtod(str.c_str(), &end_ptr);
if (*end_ptr != '\0') if (*end_ptr != '\0')
XMLLog(EError, "Invalid floating point value specified (in <%s>)", name.c_str()); XMLLog(EError, "Invalid floating point value specified (in <%s>)", name.c_str());
@ -196,7 +199,6 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
ParseContext context((name == "scene") ? NULL : &m_context.top()); ParseContext context((name == "scene") ? NULL : &m_context.top());
/* Convert attributes to ISO-8859-1 */
for (size_t i=0; i<xmlAttributes.getLength(); i++) { for (size_t i=0; i<xmlAttributes.getLength(); i++) {
std::string attrValue = transcode(xmlAttributes.getValue(i)); std::string attrValue = transcode(xmlAttributes.getValue(i));
if (attrValue.length() > 0 && attrValue.find('$') != attrValue.npos) { if (attrValue.length() > 0 && attrValue.find('$') != attrValue.npos) {
@ -208,7 +210,7 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
++pos; ++pos;
} }
} }
if (attrValue.find('$') != attrValue.npos) if (attrValue.find('$') != attrValue.npos && attrValue.find('[') == attrValue.npos)
XMLLog(EError, "The scene referenced an undefined parameter: \"%s\"", attrValue.c_str()); XMLLog(EError, "The scene referenced an undefined parameter: \"%s\"", attrValue.c_str());
} }
@ -252,6 +254,19 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
case ETransform: case ETransform:
m_transform = Transform(); m_transform = Transform();
break; break;
case EAnimation: {
m_animatedTransform = new AnimatedTransform();
ref<VectorTrack> translation = new VectorTrack(VectorTrack::ETranslationXYZ);
ref<QuatTrack> rotation = new QuatTrack(VectorTrack::ERotationQuat);
ref<VectorTrack> scaling = new VectorTrack(VectorTrack::EScaleXYZ);
translation->reserve(2);
rotation->reserve(2);
scaling->reserve(2);
m_animatedTransform->addTrack(translation);
m_animatedTransform->addTrack(rotation);
m_animatedTransform->addTrack(scaling);
}
break;
default: default:
break; break;
} }
@ -271,7 +286,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
if (context.attributes.find("id") != context.attributes.end()) if (context.attributes.find("id") != context.attributes.end())
context.properties.setID(context.attributes["id"]); context.properties.setID(context.attributes["id"]);
ref<ConfigurableObject> object = NULL; ref<ConfigurableObject> object;
TagMap::const_iterator it = m_tags.find(name); TagMap::const_iterator it = m_tags.find(name);
if (it == m_tags.end()) if (it == m_tags.end())
@ -591,9 +606,56 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
} }
break; break;
case EAnimation: {
m_animatedTransform->sortAndSimplify();
context.parent->properties.setAnimatedTransform(
context.attributes["name"], m_animatedTransform);
m_animatedTransform = NULL;
}
break;
case ETransform: { case ETransform: {
context.parent->properties.setTransform( if (!m_animatedTransform.get()) {
context.attributes["name"], m_transform); context.parent->properties.setTransform(
context.attributes["name"], m_transform);
} else {
/* Compute the polar decomposition and insert into the animated transform
uh oh.. we have to get rid of 2 matrix libraries at some point :) */
typedef Eigen::Matrix<Float, 3, 3> EMatrix;
const Matrix4x4 m = m_transform.getMatrix();
EMatrix A;
A << m(0, 0), m(0, 1), m(0, 2),
m(1, 0), m(1, 1), m(1, 2),
m(2, 0), m(2, 1), m(2, 2);
Eigen::JacobiSVD<EMatrix> svd(A, Eigen::ComputeFullU | Eigen::ComputeFullV);
EMatrix U = svd.matrixU(), V = svd.matrixV(), S = svd.singularValues().asDiagonal();
if (svd.singularValues().prod() < 0) {
S = -S; U = -U;
}
EMatrix Q = U*V.transpose();
EMatrix P = V*S*V.transpose();
VectorTrack *translation = (VectorTrack *) m_animatedTransform->getTrack(0);
QuatTrack *rotation = (QuatTrack *) m_animatedTransform->getTrack(1);
VectorTrack *scaling = (VectorTrack *) m_animatedTransform->getTrack(2);
Float time = parseFloat("time", context.attributes["time"]);
rotation->append(time, Quaternion::fromMatrix(
Matrix4x4(
Q(0, 0), Q(0, 1), Q(0, 2), 0.0f,
Q(1, 0), Q(1, 1), Q(1, 2), 0.0f,
Q(2, 0), Q(2, 1), Q(2, 2), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
)
));
scaling->append(time, Vector(P(0, 0), P(1, 1), P(2, 2)));
translation->append(time, Vector(m(0, 3), m(1, 3), m(2, 3)));
}
} }
break; break;
@ -639,11 +701,61 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
} }
break; break;
default: default: {
if (tag.second == NULL) if (tag.second == NULL)
XMLLog(EError, "Internal error: could not instantiate an object " XMLLog(EError, "Internal error: could not instantiate an object "
"corresponding to the tag '%s'", name.c_str()); "corresponding to the tag '%s'", name.c_str());
object = m_pluginManager->createObject(tag.second, context.properties);
Properties &props = context.properties;
/* Convenience hack: allow passing animated transforms to arbitrary shapes
and then internally rewrite this into a shape group + animated instance */
if (tag.second == MTS_CLASS(Shape)
&& props.hasProperty("toWorld")
&& props.getType("toWorld") == Properties::EAnimatedTransform
&& (props.getPluginName() != "instance" && props.getPluginName() != "disk")) {
/* (The 'disk' plugin also directly supports animated transformations, so
the instancing trick isn't required for it) */
ref<const AnimatedTransform> trafo = props.getAnimatedTransform("toWorld");
props.removeProperty("toWorld");
if (trafo->isStatic())
props.setTransform("toWorld", trafo->eval(0));
object = m_pluginManager->createObject(tag.second, props);
if (!trafo->isStatic()) {
object = m_pluginManager->createObject(tag.second, props);
/* If the object has children, append them */
for (std::vector<std::pair<std::string, ConfigurableObject *> >
::iterator it = context.children.begin();
it != context.children.end(); ++it) {
if (it->second != NULL) {
object->addChild(it->first, it->second);
it->second->setParent(object);
it->second->decRef();
}
}
context.children.clear();
object->configure();
ref<Shape> shapeGroup = static_cast<Shape *> (
m_pluginManager->createObject(MTS_CLASS(Shape), Properties("shapegroup")));
shapeGroup->addChild(object);
shapeGroup->configure();
Properties instanceProps("instance");
instanceProps.setAnimatedTransform("toWorld", trafo);
object = m_pluginManager->createObject(instanceProps);
object->addChild(shapeGroup);
}
} else {
object = m_pluginManager->createObject(tag.second, props);
}
}
break; break;
} }

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/plugin.h> #include <mitsuba/core/plugin.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>

View File

@ -30,7 +30,7 @@ static void appendVPL(const Scene *scene, Random *random,
const Sensor *sensor = scene->getSensor(); const Sensor *sensor = scene->getSensor();
Float time = sensor->getShutterOpen() Float time = sensor->getShutterOpen()
+ 0.5f * sensor->getShutterOpenTime(); + sensor->getShutterOpenTime() * random->nextFloat();
if (prune) { if (prune) {
/* Possibly reject VPLs if they are unlikely to be /* Possibly reject VPLs if they are unlikely to be
@ -86,11 +86,12 @@ size_t generateVPLs(const Scene *scene, Random *random,
sampler = static_cast<Sampler *> (PluginManager::getInstance()-> sampler = static_cast<Sampler *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Sampler), props)); createObject(MTS_CLASS(Sampler), props));
sampler->configure(); sampler->configure();
sampler->generate(Point2i(0));
} }
const Sensor *sensor = scene->getSensor(); const Sensor *sensor = scene->getSensor();
Float time = sensor->getShutterOpen() Float time = sensor->getShutterOpen()
+ 0.5f * sensor->getShutterOpenTime(); + sensor->getShutterOpenTime() * sampler->next1D();
const Frame stdFrame(Vector(1,0,0), Vector(0,1,0), Vector(0,0,1)); const Frame stdFrame(Vector(1,0,0), Vector(0,1,0), Vector(0,0,1));
@ -110,6 +111,7 @@ size_t generateVPLs(const Scene *scene, Random *random,
if (!emitter->isEnvironmentEmitter() && emitter->needsDirectionSample()) { if (!emitter->isEnvironmentEmitter() && emitter->needsDirectionSample()) {
VPL lumVPL(EPointEmitterVPL, weight); VPL lumVPL(EPointEmitterVPL, weight);
lumVPL.its.p = pRec.p; lumVPL.its.p = pRec.p;
lumVPL.its.time = time;
lumVPL.its.shFrame = pRec.n.isZero() ? stdFrame : Frame(pRec.n); lumVPL.its.shFrame = pRec.n.isZero() ? stdFrame : Frame(pRec.n);
lumVPL.emitter = emitter; lumVPL.emitter = emitter;
appendVPL(scene, random, lumVPL, prune, vpls); appendVPL(scene, random, lumVPL, prune, vpls);
@ -128,6 +130,7 @@ size_t generateVPLs(const Scene *scene, Random *random,
VPL lumVPL(EDirectionalEmitterVPL, weight2); VPL lumVPL(EDirectionalEmitterVPL, weight2);
lumVPL.its.p = Point(0.0); lumVPL.its.p = Point(0.0);
lumVPL.its.time = time;
lumVPL.its.shFrame = Frame(-diRec.d); lumVPL.its.shFrame = Frame(-diRec.d);
lumVPL.emitter = emitter; lumVPL.emitter = emitter;
appendVPL(scene, random, lumVPL, false, vpls); appendVPL(scene, random, lumVPL, false, vpls);

View File

@ -1,4 +1,4 @@
Import('env', 'os', 'glob', 'sys', 'hasQt', 'hasCollada', 'hasBreakpad', 'mainEnv', Import('env', 'os', 'glob', 'sys', 'hasQt', 'hasCollada', 'hasBreakpad', 'mainEnv',
'resources', 'converter_objects') 'resources', 'converter_objects')
# For running Uic & Moc (below) # For running Uic & Moc (below)
@ -58,7 +58,7 @@ if hasQt:
qtEnv.Prepend(LIBPATH=env['COLLADALIBDIR']) qtEnv.Prepend(LIBPATH=env['COLLADALIBDIR'])
if env.has_key('COLLADALIB'): if env.has_key('COLLADALIB'):
qtEnv.Prepend(LIBS=env['COLLADALIB']) qtEnv.Prepend(LIBS=env['COLLADALIB'])
if sys.platform == 'darwin': if sys.platform == 'darwin':
mainEnv_osx = mainEnv.Clone() mainEnv_osx = mainEnv.Clone()
qtEnv_osx = qtEnv.Clone() qtEnv_osx = qtEnv.Clone()

View File

@ -78,6 +78,31 @@ static void setProperties(QDomDocument &doc, QDomElement &element,
property = doc.createElement("string"); property = doc.createElement("string");
property.setAttribute("value", props.getString(*it).c_str()); property.setAttribute("value", props.getString(*it).c_str());
break; break;
case Properties::EAnimatedTransform: {
const AnimatedTransform *trafo = props.getAnimatedTransform(*it);
std::set<Float> times;
trafo->collectKeyframes(times);
property = doc.createElement("animation");
for (std::set<Float>::iterator it2 = times.begin(); it2 != times.end(); ++it2) {
const Matrix4x4 &matrix = trafo->eval(*it2).getMatrix();
QDomElement trafoTag = doc.createElement("transform");
QDomElement matrixTag = doc.createElement("matrix");
QString value;
for (int i=0; i<4; ++i)
for (int j=0; j<4; ++j)
value += QString("%1 ").arg(matrix(i, j));
matrixTag.setAttribute("value", value);
trafoTag.setAttribute("time", *it2);
trafoTag.appendChild(matrixTag);
property.appendChild(trafoTag);
}
}
break;
case Properties::ETransform: { case Properties::ETransform: {
/* Captures the subset of transformations that are used by /* Captures the subset of transformations that are used by
Mitsuba's perspective and orthographic camera classes */ Mitsuba's perspective and orthographic camera classes */

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/warp.h> #include <mitsuba/core/warp.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/frame.h> #include <mitsuba/core/frame.h>
#include <mitsuba/core/warp.h> #include <mitsuba/core/warp.h>

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/frame.h> #include <mitsuba/core/frame.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -113,7 +113,7 @@ public:
foreshortening terms caused by the aperture, hence the flag "EOnSurface" */ foreshortening terms caused by the aperture, hence the flag "EOnSurface" */
m_type |= EDeltaPosition | EPerspectiveCamera | EOnSurface | EDirectionSampleMapsToPixels; m_type |= EDeltaPosition | EPerspectiveCamera | EOnSurface | EDirectionSampleMapsToPixels;
if (props.getTransform("toWorld", Transform()).hasScale()) if (props.getAnimatedTransform("toWorld", Transform())->eval(0).hasScale())
Log(EError, "Scale factors in the camera-to-world " Log(EError, "Scale factors in the camera-to-world "
"transformation are not allowed!"); "transformation are not allowed!");
} }

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/core/frame.h> #include <mitsuba/core/frame.h>
#include <mitsuba/core/warp.h> #include <mitsuba/core/warp.h>

View File

@ -18,7 +18,7 @@
#include <mitsuba/render/sensor.h> #include <mitsuba/render/sensor.h>
#include <mitsuba/render/medium.h> #include <mitsuba/render/medium.h>
#include <mitsuba/render/track.h> #include <mitsuba/core/track.h>
#include <mitsuba/render/shape.h> #include <mitsuba/render/shape.h>
#include <mitsuba/core/frame.h> #include <mitsuba/core/frame.h>
#include <mitsuba/core/plugin.h> #include <mitsuba/core/plugin.h>
@ -137,7 +137,7 @@ public:
m_apertureRadius = Epsilon; m_apertureRadius = Epsilon;
} }
if (props.getTransform("toWorld", Transform()).hasScale()) if (props.getAnimatedTransform("toWorld", Transform())->eval(0).hasScale())
Log(EError, "Scale factors in the camera-to-world " Log(EError, "Scale factors in the camera-to-world "
"transformation are not allowed!"); "transformation are not allowed!");
} }
@ -520,14 +520,11 @@ public:
} }
ref<Shape> createShape(const Scene *scene) { ref<Shape> createShape(const Scene *scene) {
if (!m_worldTransform->isStatic()) ref<AnimatedTransform> trafo = new AnimatedTransform(m_worldTransform);
Log(EError, "Bidirectional renderings involving moving " trafo->prependScale(Vector(m_apertureRadius));
"perspective cameras with depth of field are currently not supported!");
Transform trafo = m_worldTransform->eval(0) *
Transform::scale(Vector(m_apertureRadius));
Properties props("disk"); Properties props("disk");
props.setTransform("toWorld", trafo); props.setAnimatedTransform("toWorld", trafo);
Shape *shape = static_cast<Shape *> (PluginManager::getInstance()-> Shape *shape = static_cast<Shape *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Shape), props)); createObject(MTS_CLASS(Shape), props));
shape->addChild(this); shape->addChild(this);

View File

@ -11,6 +11,6 @@ plugins += env.SharedLibrary('cylinder', ['cylinder.cpp'])
plugins += env.SharedLibrary('hair', ['hair.cpp']) plugins += env.SharedLibrary('hair', ['hair.cpp'])
plugins += env.SharedLibrary('shapegroup', ['shapegroup.cpp']) plugins += env.SharedLibrary('shapegroup', ['shapegroup.cpp'])
plugins += env.SharedLibrary('instance', ['instance.cpp']) plugins += env.SharedLibrary('instance', ['instance.cpp'])
plugins += env.SharedLibrary('animatedinstance', ['animatedinstance.cpp']) #plugins += env.SharedLibrary('deformable', ['deformable.cpp'])
Export('plugins') Export('plugins')

View File

@ -1,164 +0,0 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/render/track.h>
#include <mitsuba/core/fresolver.h>
#include <mitsuba/core/fstream.h>
#include "shapegroup.h"
MTS_NAMESPACE_BEGIN
/*!\plugin{animatedinstance}{Animated geometry instance}
* \order{10}
* \parameters{
* \parameter{filename}{\String}{Filename of an animated
* transformation}
* \parameter{\Unnamed}{\ShapeGroup}{A reference to a
* shape group that should be instantiated}
* }
*
* This plugin implements an \emph{animated} geometry instance,
* i.e. one or more shapes that are undergoing \emph{ridgid}
* transformations over time.
*
* The input file should contain a binary / serialized
* \code{AnimatedTransform} data structure -- for details,
* please refer to the C++ implementation of this class.
*/
class AnimatedInstance : public Shape {
public:
AnimatedInstance(const Properties &props) : Shape(props) {
FileResolver *fResolver = Thread::getThread()->getFileResolver();
fs::path path = fResolver->resolve(props.getString("filename"));
m_name = path.filename().string();
Log(EInfo, "Loading animation track from \"%s\"", m_name.c_str());
ref<FileStream> fs = new FileStream(path, FileStream::EReadOnly);
m_transform = new AnimatedTransform(fs);
}
AnimatedInstance(Stream *stream, InstanceManager *manager)
: Shape(stream, manager) {
m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream));
m_transform = new AnimatedTransform(stream);
configure();
}
void serialize(Stream *stream, InstanceManager *manager) const {
Shape::serialize(stream, manager);
manager->serialize(stream, m_shapeGroup.get());
m_transform->serialize(stream);
}
void configure() {
if (!m_shapeGroup)
Log(EError, "A reference to a 'shapegroup' must be specified!");
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
m_aabb = m_transform->getSpatialBounds(kdtree->getAABB());
}
AABB getAABB() const {
return m_aabb;
}
std::string getName() const {
return m_name;
}
Float getSurfaceArea() const {
Log(EError, "AnimatedInstance::getSurfaceArea(): not supported!");
return 0.0f;
}
void addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->getName() == "ShapeGroup") {
m_shapeGroup = static_cast<ShapeGroup *>(child);
} else {
Shape::addChild(name, child);
}
}
bool rayIntersect(const Ray &_ray, Float mint,
Float maxt, Float &t, void *temp) const {
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
Ray ray;
const Transform &objectToWorld = m_transform->eval(_ray.time);
Transform worldToObject = objectToWorld.inverse();
worldToObject.transformAffine(_ray, ray);
return kdtree->rayIntersect(ray, mint, maxt, t, temp);
}
bool rayIntersect(const Ray &_ray, Float mint, Float maxt) const {
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
Ray ray;
const Transform &objectToWorld = m_transform->eval(_ray.time);
Transform worldToObject = objectToWorld.inverse();
worldToObject.transformAffine(_ray, ray);
return kdtree->rayIntersect(ray, mint, maxt);
}
void fillIntersectionRecord(const Ray &_ray,
const void *temp, Intersection &its) const {
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
const Transform &objectToWorld = m_transform->eval(_ray.time);
Ray ray;
objectToWorld.inverse()(_ray, ray);
kdtree->fillIntersectionRecord<false>(ray, temp, its);
its.shFrame.n = normalize(objectToWorld(its.shFrame.n));
its.shFrame.s = normalize(objectToWorld(its.shFrame.s));
its.shFrame.t = normalize(objectToWorld(its.shFrame.t));
its.geoFrame = Frame(normalize(objectToWorld(its.geoFrame.n)));
its.dpdu = objectToWorld(its.dpdu);
its.dpdv = objectToWorld(its.dpdv);
its.wi = normalize(its.shFrame.toLocal(-_ray.d));
its.instance = this;
}
void getNormalDerivative(const Intersection &its,
Vector &dndu, Vector &dndv, bool shadingFrame) const {
const Transform &objectToWorld = m_transform->eval(its.time);
its.shape->getNormalDerivative(its, dndu, dndv, shadingFrame);
/* The following will probably be incorrect for
non-rigid transformations */
dndu = objectToWorld(dndu);
dndv = objectToWorld(dndv);
}
size_t getPrimitiveCount() const {
return 0;
}
size_t getEffectivePrimitiveCount() const {
return m_shapeGroup->getPrimitiveCount();
}
MTS_DECLARE_CLASS()
private:
ref<ShapeGroup> m_shapeGroup;
ref<AnimatedTransform> m_transform;
AABB m_aabb;
std::string m_name;
};
MTS_IMPLEMENT_CLASS_S(AnimatedInstance, false, Shape)
MTS_EXPORT_PLUGIN(AnimatedInstance, "Animated instanced geometry");
MTS_NAMESPACE_END

View File

@ -43,7 +43,7 @@ MTS_NAMESPACE_BEGIN
* Is the cylinder inverted, i.e. should the normal vectors * Is the cylinder inverted, i.e. should the normal vectors
* be flipped? \default{\code{false}, i.e. the normals point outside} * be flipped? \default{\code{false}, i.e. the normals point outside}
* } * }
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional linear object-to-world transformation. * Specifies an optional linear object-to-world transformation.
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}

467
src/shapes/deformable.cpp Normal file
View File

@ -0,0 +1,467 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/render/shape.h>
#include <mitsuba/render/sahkdtree4.h>
#include <mitsuba/render/trimesh.h>
#include <mitsuba/core/properties.h>
#include <mitsuba/core/fresolver.h>
#include <mitsuba/core/mstream.h>
#include <mitsuba/core/mmap.h>
#define SHAPE_PER_SEGMENT 1
#define NO_CLIPPING_SUPPORT 1
MTS_NAMESPACE_BEGIN
class SpaceTimeKDTree : public SAHKDTree4D<SpaceTimeKDTree> {
friend class GenericKDTree<AABB4, SurfaceAreaHeuristic4, SpaceTimeKDTree>;
friend class SAHKDTree4D<SpaceTimeKDTree>;
public:
/// Temporarily holds some intersection information
struct IntersectionCache {
Point p[3];
Float u, v;
};
SpaceTimeKDTree(const std::vector<Float> &frameTimes, std::vector<float *> &positions,
Triangle *triangles, size_t vertexCount, size_t triangleCount)
: m_frameTimes(frameTimes), m_positions(positions), m_triangles(triangles),
m_vertexCount(vertexCount), m_triangleCount(triangleCount) {
Log(EInfo, "Total amount of vertex data: %s",
memString(vertexCount*frameTimes.size()*sizeof(float)*3).c_str());
//setClip(false);
//setExactPrimitiveThreshold(10);
buildInternal();
/* Collect some statistics */
std::stack<const KDNode *> stack;
stack.push(m_nodes);
size_t spatialSplits = 0, timeSplits = 0;
while (!stack.empty()) {
const KDNode *node = stack.top();
stack.pop();
if (!node->isLeaf()) {
if (node->getAxis() == 3) {
timeSplits++;
} else {
spatialSplits++;
}
stack.push((const KDNode *) node->getLeft());
stack.push((const KDNode *) node->getRight());
}
}
KDLog(EInfo, "Spacetime kd-tree statistics");
KDLog(EInfo, " Time interval = [%f, %f]" , m_tightAABB.min.w, m_tightAABB.max.w);
KDLog(EInfo, " Spatial splits = " SIZE_T_FMT, spatialSplits);
KDLog(EInfo, " Time splits = " SIZE_T_FMT, timeSplits);
KDLog(EInfo, "");
m_spatialAABB = AABB(
Point(m_aabb.min.x, m_aabb.min.y, m_aabb.min.z),
Point(m_aabb.max.x, m_aabb.max.y, m_aabb.max.z)
);
}
/// Return one of the points stored in the point cache
inline Point getPoint(uint32_t frame, uint32_t index) const {
float *ptr = m_positions[frame] + index*3;
#if defined(__LITTLE_ENDIAN__)
return Point(
(Float) endianness_swap(ptr[0]),
(Float) endianness_swap(ptr[1]),
(Float) endianness_swap(ptr[2]));
#else
return Point((Float) ptr[0], (Float) ptr[1], (Float) ptr[2]);
#endif
}
// ========================================================================
// Implementation of functions required by the parent class
// ========================================================================
/// Return the total number of primitives that are organized in the tree
inline SizeType getPrimitiveCount() const {
#ifdef SHAPE_PER_SEGMENT
return m_triangleCount * (m_frameTimes.size() - 1);
#else
return m_triangleCount;
#endif
}
/// Return the 4D extents for one of the primitives contained in the tree
AABB4 getAABB(IndexType index) const {
#ifdef SHAPE_PER_SEGMENT
int frameIdx = index / m_triangleCount;
int triangleIdx = index % m_triangleCount;
const Triangle &tri = m_triangles[triangleIdx];
AABB aabb;
for (int i=0; i<3; ++i) {
aabb.expandBy(getPoint(frameIdx, tri.idx[i]));
aabb.expandBy(getPoint(frameIdx+1, tri.idx[i]));
}
return AABB4(
Point4(aabb.min.x, aabb.min.y, aabb.min.z, m_frameTimes[frameIdx]),
Point4(aabb.max.x, aabb.max.y, aabb.max.z, m_frameTimes[frameIdx+1])
);
#else
AABB aabb;
const Triangle &tri = m_triangles[index];
for (size_t i=0; i<m_frameTimes.size(); ++i)
for (int j=0; j<3; ++j)
aabb.expandBy(getPoint(i, tri.idx[j]));
return AABB4(
Point4(aabb.min.x, aabb.min.y, aabb.min.z, m_frameTimes[0]),
Point4(aabb.max.x, aabb.max.y, aabb.max.z, m_frameTimes[m_frameTimes.size()-1])
);
#endif
}
/// Return a clipped 4D AABB for one of the primitives contained in the tree
AABB4 getClippedAABB(int index, const AABB4 &box) const {
AABB clip(
Point(box.min.x, box.min.y, box.min.z),
Point(box.max.x, box.max.y, box.max.z)
);
#ifdef NO_CLIPPING_SUPPORT
AABB4 aabb = getAABB(index);
aabb.clip(box);
return aabb;
#elif SHAPE_PER_SEGMENT
int frameIdx = index / m_triangleCount;
int triangleIdx = index % m_triangleCount;
AABB aabb(m_triangles[triangleIdx].getClippedAABB(m_positions[frameIdx], clip)); /// XXX broken
aabb.expandBy(m_triangles[triangleIdx].getClippedAABB(m_positions[frameIdx+1], clip));
if (aabb.isValid())
return AABB4(
Point4(aabb.min.x, aabb.min.y, aabb.min.z, box.min.w),
Point4(aabb.max.x, aabb.max.y, aabb.max.z, box.max.w));
else
return AABB4();
#else
int startIndex = std::max((int) (std::lower_bound(m_frameTimes.begin(), m_frameTimes.end(),
box.min.w) - m_frameTimes.begin()) - 1, 0);
int endIndex = (int) (std::lower_bound(m_frameTimes.begin(), m_frameTimes.end(),
box.max.w) - m_frameTimes.begin());
AABB4 result;
const Triangle &tri = m_triangles[index];
for (int i=startIndex; i<=endIndex; ++i) {
Point p0 = getPoint(i, tri.idx[0]);
Point p1 = getPoint(i, tri.idx[1]);
Point p2 = getPoint(i, tri.idx[2]);
AABB aabb(Triangle::getClippedAABB(p0, p1, p2, clip));
if (aabb.isValid()) {
result.expandBy(Point4(aabb.min.x, aabb.min.y, aabb.min.z, m_frameTimes[i]));
result.expandBy(Point4(aabb.max.x, aabb.max.y, aabb.max.z, m_frameTimes[i]));
}
}
result.clip(box);
return result;
#endif
}
/// Cast a normal (i.e. non-shadow) ray against a specific animated triangle
inline bool intersect(const Ray &ray, IndexType idx,
Float mint, Float maxt, Float &t, void *tmp) const {
#if SHAPE_PER_SEGMENT
IndexType frameIdx = idx / m_triangleCount;
IndexType triangleIdx = idx % m_triangleCount;
#else
IndexType triangleIdx = idx;
IndexType frameIdx = (IndexType) std::max((int) (std::lower_bound(
m_frameTimes.begin(), m_frameTimes.end(), ray.time) -
m_frameTimes.begin()) - 1, 0);
#endif
const Triangle &tri = m_triangles[triangleIdx];
Float alpha = (ray.time - m_frameTimes[frameIdx])
/ (m_frameTimes[frameIdx + 1] - m_frameTimes[frameIdx]);
if (alpha < 0 || alpha > 1)
return false;
/* Compute interpolated positions */
Point p[3];
for (int i=0; i<3; ++i)
p[i] = (1 - alpha) * getPoint(frameIdx, tri.idx[i])
+ alpha * getPoint(frameIdx+1, tri.idx[i]);
Float tempU, tempV, tempT;
if (!Triangle::rayIntersect(p[0], p[1], p[2], ray, tempU, tempV, tempT))
return false;
if (tempT < mint || tempT > maxt)
return false;
if (tmp != NULL) {
IntersectionCache *cache =
static_cast<IntersectionCache *>(tmp);
t = tempT;
memcpy(cache->p, p, sizeof(Point)*3);
cache->u = tempU;
cache->v = tempV;
}
return true;
}
/// Cast a shadow ray against a specific triangle
inline bool intersect(const Ray &ray, IndexType idx,
Float mint, Float maxt) const {
Float tempT;
/* No optimized version for shadow rays yet */
return intersect(ray, idx, mint, maxt, tempT, NULL);
}
// ========================================================================
// Miscellaneous
// ========================================================================
/// Intersect a ray with all primitives stored in the kd-tree
inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt,
Float &t, void *temp) const {
Float tempT = std::numeric_limits<Float>::infinity();
Float mint, maxt;
if (m_spatialAABB.rayIntersect(ray, mint, maxt)) {
if (_mint > mint) mint = _mint;
if (_maxt < maxt) maxt = _maxt;
if (EXPECT_TAKEN(maxt > mint && ray.time >= m_aabb.min.w && ray.time <= m_aabb.max.w)) {
if (rayIntersectHavran<false>(ray, mint, maxt, tempT, temp)) {
t = tempT;
return true;
}
}
}
return false;
}
/**
* \brief Intersect a ray with all primitives stored in the kd-tree
* (Visiblity query version)
*/
inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt) const {
Float tempT = std::numeric_limits<Float>::infinity();
Float mint, maxt;
if (m_spatialAABB.rayIntersect(ray, mint, maxt)) {
if (_mint > mint) mint = _mint;
if (_maxt < maxt) maxt = _maxt;
if (EXPECT_TAKEN(maxt > mint && ray.time >= m_aabb.min.w && ray.time <= m_aabb.max.w))
if (rayIntersectHavran<true>(ray, mint, maxt, tempT, NULL))
return true;
}
return false;
}
inline const Triangle *getTriangles() const {
return m_triangles;
}
/// Return an AABB with the spatial extents
inline const AABB &getSpatialAABB() const {
return m_spatialAABB;
}
MTS_DECLARE_CLASS()
protected:
std::vector<Float> m_frameTimes;
std::vector<float *> m_positions;
Triangle *m_triangles;
size_t m_vertexCount;
size_t m_triangleCount;
AABB m_spatialAABB;
};
class Deformable : public Shape {
public:
Deformable(const Properties &props) : Shape(props) {
FileResolver *fResolver = Thread::getThread()->getFileResolver();
fs::path path = fResolver->resolve(props.getString("filename"));
if (path.extension() != ".mdd")
Log(EError, "Point cache files must have the extension \".mdd\"");
m_mmap = new MemoryMappedFile(path);
ref<MemoryStream> mStream = new MemoryStream((uint8_t *) m_mmap->getData(),
m_mmap->getSize());
mStream->setByteOrder(Stream::EBigEndian);
uint32_t frameCount = mStream->readUInt();
m_vertexCount = mStream->readUInt();
Log(EInfo, "Point cache has %i frames and %i vertices", frameCount, m_vertexCount);
Float clipStart = props.getFloat("clipStart", 0),
clipEnd = props.getFloat("clipEnd", 0);
std::vector<Float> frameTimes;
std::vector<float *> positions;
for (uint32_t i=0; i<frameCount; ++i)
frameTimes.push_back((Float) mStream->readSingle());
for (uint32_t i=0; i<frameCount; ++i) {
positions.push_back(reinterpret_cast<float *>(mStream->getCurrentData()));
mStream->skip(m_vertexCount * 3 * sizeof(float));
}
if (clipStart != clipEnd) {
m_positions.reserve(positions.size());
m_frameTimes.reserve(frameTimes.size());
for (uint32_t i=0; i<frameCount; ++i) {
if (frameTimes[i] >= clipStart && frameTimes[i] <= clipEnd) {
m_frameTimes.push_back(frameTimes[i]);
m_positions.push_back(positions[i]);
}
}
if (m_frameTimes.empty())
Log(EError, "After clipping to the time range [%f, %f] no frames were left!",
clipStart, clipEnd);
Log(EInfo, "Clipped away %u/%u frames", frameCount - (uint32_t) m_frameTimes.size(), frameCount);
} else {
m_positions = positions;
m_frameTimes = frameTimes;
}
}
Deformable(Stream *stream, InstanceManager *manager)
: Shape(stream, manager) {
/// TBD
}
void serialize(Stream *stream, InstanceManager *manager) const {
Shape::serialize(stream, manager);
/// TBD
}
void configure() {
Shape::configure();
if (m_mesh == NULL)
Log(EError, "A nested triangle mesh is required so that "
"connectivity information can be extracted!");
if (m_mesh->getVertexCount() != m_vertexCount)
Log(EError, "Point cache and nested geometry have mismatched "
"numbers of vertices!");
m_kdtree = new SpaceTimeKDTree(m_frameTimes, m_positions, m_mesh->getTriangles(),
m_vertexCount, m_mesh->getTriangleCount());
m_aabb = m_kdtree->getSpatialAABB();
}
bool rayIntersect(const Ray &ray, Float mint,
Float maxt, Float &t, void *temp) const {
return m_kdtree->rayIntersect(ray, mint, maxt, t, temp);
}
bool rayIntersect(const Ray &ray, Float mint, Float maxt) const {
return m_kdtree->rayIntersect(ray, mint, maxt);
}
void fillIntersectionRecord(const Ray &ray,
const void *temp, Intersection &its) const {
const SpaceTimeKDTree::IntersectionCache *cache
= reinterpret_cast<const SpaceTimeKDTree::IntersectionCache *>(temp);
const Vector b(1 - cache->u - cache->v, cache->u, cache->v);
const Point p0 = cache->p[0];
const Point p1 = cache->p[1];
const Point p2 = cache->p[2];
Normal faceNormal(cross(p1-p0, p2-p0));
Float length = faceNormal.length();
if (!faceNormal.isZero())
faceNormal /= length;
/* Just the basic attributes for now and geometric normals */
its.p = ray(its.t);
its.geoFrame = Frame(faceNormal);
its.shFrame = its.geoFrame;
its.wi = its.toLocal(-ray.d);
its.shape = this;
its.instance = this;
its.hasUVPartials = false;
its.time = ray.time;
}
AABB getAABB() const {
return m_kdtree->getSpatialAABB();
}
size_t getPrimitiveCount() const {
return m_mesh->getTriangleCount();
}
size_t getEffectivePrimitiveCount() const {
return m_mesh->getTriangleCount();
}
void addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->derivesFrom(TriMesh::m_theClass)) {
Assert(m_mesh == NULL);
m_mesh = static_cast<TriMesh *>(child);
if (m_mesh->getVertexCount() != m_vertexCount)
Log(EError, "Geometry mismatch! MDD file contains %u vertices. "
"The attached shape uses %u!", m_vertexCount, m_mesh->getVertexCount());
} else if (cClass->derivesFrom(Shape::m_theClass) && static_cast<Shape *>(child)->isCompound()) {
size_t index = 0;
Shape *shape = static_cast<Shape *>(child);
do {
ref<Shape> element = shape->getElement(index++);
if (element == NULL)
break;
addChild(name, element);
} while (true);
} else {
Shape::addChild(name, child);
}
}
std::string toString() const {
std::ostringstream oss;
oss << "Deformable[" << endl
<< " mesh = " << indent(m_mesh.toString()) << endl
<< "]";
return oss.str();
}
MTS_DECLARE_CLASS()
private:
ref<MemoryMappedFile> m_mmap;
ref<SpaceTimeKDTree> m_kdtree;
std::vector<Float> m_frameTimes;
std::vector<float *> m_positions;
ref<TriMesh> m_mesh;
uint32_t m_vertexCount;
AABB m_aabb;
};
MTS_IMPLEMENT_CLASS(SpaceTimeKDTree, false, KDTreeBase)
MTS_IMPLEMENT_CLASS_S(Deformable, false, Shape)
MTS_EXPORT_PLUGIN(Deformable, "Deformable shape");
MTS_NAMESPACE_END

View File

@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN
/*!\plugin{disk}{Disk intersection primitive} /*!\plugin{disk}{Disk intersection primitive}
* \order{4} * \order{4}
* \parameters{ * \parameters{
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies a linear object-to-world transformation. * Specifies a linear object-to-world transformation.
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}
@ -81,31 +81,29 @@ MTS_NAMESPACE_BEGIN
class Disk : public Shape { class Disk : public Shape {
public: public:
Disk(const Properties &props) : Shape(props) { Disk(const Properties &props) : Shape(props) {
m_objectToWorld = props.getTransform("toWorld", Transform()); m_objectToWorld = new AnimatedTransform(props.getAnimatedTransform("toWorld", Transform()));
if (props.getBoolean("flipNormals", false)) if (props.getBoolean("flipNormals", false))
m_objectToWorld = m_objectToWorld * Transform::scale(Vector(1, 1, -1)); m_objectToWorld->prependScale(Vector(1, 1, -1));
m_worldToObject = m_objectToWorld.inverse();
} }
Disk(Stream *stream, InstanceManager *manager) Disk(Stream *stream, InstanceManager *manager)
: Shape(stream, manager) { : Shape(stream, manager) {
m_objectToWorld = Transform(stream); m_objectToWorld = new AnimatedTransform(stream);
m_worldToObject = m_objectToWorld.inverse();
configure(); configure();
} }
void serialize(Stream *stream, InstanceManager *manager) const { void serialize(Stream *stream, InstanceManager *manager) const {
Shape::serialize(stream, manager); Shape::serialize(stream, manager);
m_objectToWorld.serialize(stream); m_objectToWorld->serialize(stream);
} }
void configure() { void configure() {
Shape::configure(); Shape::configure();
m_normal = normalize(m_objectToWorld(Normal(0, 0, 1))); const Transform &trafo = m_objectToWorld->eval(0);
Vector dpdu = trafo(Vector(1, 0, 0));
Vector dpdu = m_objectToWorld(Vector(1, 0, 0)); Vector dpdv = trafo(Vector(0, 1, 0));
Vector dpdv = m_objectToWorld(Vector(0, 1, 0));
if (std::abs(dot(dpdu, dpdv)) > 1e-3f) if (std::abs(dot(dpdu, dpdv)) > 1e-3f)
Log(EError, "Error: 'toWorld' transformation contains shear!"); Log(EError, "Error: 'toWorld' transformation contains shear!");
@ -117,23 +115,30 @@ public:
} }
AABB getAABB() const { AABB getAABB() const {
std::set<Float> times;
m_objectToWorld->collectKeyframes(times);
AABB aabb; AABB aabb;
aabb.expandBy(m_objectToWorld(Point( 1, 0, 0))); for (std::set<Float>::iterator it = times.begin(); it != times.end(); ++it) {
aabb.expandBy(m_objectToWorld(Point(-1, 0, 0))); const Transform &trafo = m_objectToWorld->eval(*it);
aabb.expandBy(m_objectToWorld(Point( 0, 1, 0))); aabb.expandBy(trafo(Point( 1, 0, 0)));
aabb.expandBy(m_objectToWorld(Point( 0, -1, 0))); aabb.expandBy(trafo(Point(-1, 0, 0)));
aabb.expandBy(trafo(Point( 0, 1, 0)));
aabb.expandBy(trafo(Point( 0, -1, 0)));
}
return aabb; return aabb;
} }
Float getSurfaceArea() const { Float getSurfaceArea() const {
Vector dpdu = m_objectToWorld(Vector(1, 0, 0)); const Transform &trafo = m_objectToWorld->eval(0);
Vector dpdv = m_objectToWorld(Vector(0, 1, 0)); Vector dpdu = trafo(Vector(1, 0, 0));
Vector dpdv = trafo(Vector(0, 1, 0));
return M_PI * dpdu.length() * dpdv.length(); return M_PI * dpdu.length() * dpdv.length();
} }
inline bool rayIntersect(const Ray &_ray, Float mint, Float maxt, Float &t, void *temp) const { inline bool rayIntersect(const Ray &_ray, Float mint, Float maxt, Float &t, void *temp) const {
Ray ray; Ray ray;
m_worldToObject.transformAffine(_ray, ray); m_objectToWorld->eval(ray.time).inverse().transformAffine(_ray, ray);
Float hit = -ray.o.z / ray.d.z; Float hit = -ray.o.z / ray.d.z;
if (!(hit >= mint && hit <= maxt)) if (!(hit >= mint && hit <= maxt))
@ -173,18 +178,20 @@ public:
phi += 2*M_PI; phi += 2*M_PI;
Float cosPhi = data[0] * invR, sinPhi = data[1] * invR; Float cosPhi = data[0] * invR, sinPhi = data[1] * invR;
const Transform &trafo = m_objectToWorld->eval(ray.time);
its.shape = this; its.shape = this;
if (r != 0) { if (r != 0) {
its.dpdu = m_objectToWorld(Vector(cosPhi, sinPhi, 0)); its.dpdu = trafo(Vector(cosPhi, sinPhi, 0));
its.dpdv = m_objectToWorld(Vector(-sinPhi, cosPhi, 0)); its.dpdv = trafo(Vector(-sinPhi, cosPhi, 0));
} else { } else {
its.dpdu = m_objectToWorld(Vector(1, 0, 0)); its.dpdu = trafo(Vector(1, 0, 0));
its.dpdv = m_objectToWorld(Vector(0, 1, 0)); its.dpdv = trafo(Vector(0, 1, 0));
} }
its.shFrame = its.geoFrame = Frame( its.shFrame = its.geoFrame = Frame(
normalize(its.dpdu), normalize(its.dpdv), m_normal); normalize(its.dpdu), normalize(its.dpdv),
normalize(trafo(Normal(0, 0, 1))));
its.uv = Point2(r, phi * INV_TWOPI); its.uv = Point2(r, phi * INV_TWOPI);
its.p = ray(its.t); its.p = ray(its.t);
its.wi = its.toLocal(-ray.d); its.wi = its.toLocal(-ray.d);
@ -206,16 +213,19 @@ public:
Float dphi = (2 * M_PI) / (Float) (phiSteps-1); Float dphi = (2 * M_PI) / (Float) (phiSteps-1);
Point center = m_objectToWorld(Point(0.0f)); const Transform &trafo = m_objectToWorld->eval(0.0f);
Point center = trafo(Point(0.0f));
Normal normal = normalize(trafo(Normal(0, 0, 1)));
for (uint32_t i=0; i<phiSteps; ++i) { for (uint32_t i=0; i<phiSteps; ++i) {
Float phi = i*dphi; Float phi = i*dphi;
vertices[i] = center; vertices[i] = center;
vertices[phiSteps+i] = m_objectToWorld( vertices[phiSteps+i] = trafo(
Point(std::cos(phi), std::sin(phi), 0) Point(std::cos(phi), std::sin(phi), 0)
); );
normals[i] = m_normal; normals[i] = normal;
normals[phiSteps+i] = m_normal; normals[phiSteps+i] = normal;
texcoords[i] = Point2(0.0f, phi * INV_TWOPI); texcoords[i] = Point2(0.0f, phi * INV_TWOPI);
texcoords[phiSteps+i] = Point2(1.0f, phi * INV_TWOPI); texcoords[phiSteps+i] = Point2(1.0f, phi * INV_TWOPI);
} }
@ -238,10 +248,11 @@ public:
} }
void samplePosition(PositionSamplingRecord &pRec, const Point2 &sample) const { void samplePosition(PositionSamplingRecord &pRec, const Point2 &sample) const {
const Transform &trafo = m_objectToWorld->eval(pRec.time);
Point2 p = Warp::squareToUniformDiskConcentric(sample); Point2 p = Warp::squareToUniformDiskConcentric(sample);
pRec.p = m_objectToWorld(Point3(p.x, p.y, 0)); pRec.p = trafo(Point3(p.x, p.y, 0));
pRec.n = m_normal; pRec.n = trafo(normalize(Normal(0,0,1)));
pRec.pdf = m_invSurfaceArea; pRec.pdf = m_invSurfaceArea;
pRec.measure = EArea; pRec.measure = EArea;
} }
@ -261,7 +272,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
@ -276,10 +287,7 @@ public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
Transform m_objectToWorld; ref<AnimatedTransform> m_objectToWorld;
Transform m_worldToObject;
Normal m_normal;
Float m_surfaceArea;
Float m_invSurfaceArea; Float m_invSurfaceArea;
}; };

View File

@ -25,33 +25,49 @@ MTS_NAMESPACE_BEGIN
* \parameters{ * \parameters{
* \parameter{\Unnamed}{\ShapeGroup}{A reference to a * \parameter{\Unnamed}{\ShapeGroup}{A reference to a
* shape group that should be instantiated} * shape group that should be instantiated}
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional linear instance-to-world transformation. * Specifies an optional linear instance-to-world transformation.
* \default{none (i.e. instance space $=$ world space)} * \default{none (i.e. instance space $=$ world space)}
* } * }
* } * }
* \renderings{
* \rendering{Surface viewed from the top}{shape_instance_fractal_top}
* \rendering{Surface viewed from the bottom}{shape_instance_fractal_bot}
* \caption{
* A visualization of a fractal surface by Irving and Segerman.
* (a 2D Gospel curve developed up to level 5 along the third
* dimension). This scene makes use of instancing to replicate
* similar structures to cheaply render a structure that effectively
* consists of several hundred millions of triangles.
* }
* }
* *
* This plugin implements a geometry instance used to efficiently replicate * This plugin implements a geometry instance used to efficiently replicate
* geometry many times. For details, please refer to the \pluginref{shapegroup} * geometry many times. For details on how to create instances, refer to
* plugin. * the \pluginref{shapegroup} plugin.
* \remarks{
* \item Note that it is \emph{not} possible to assign a different
* material to each instance --- the material assignment specified within
* the shape group is the one that matters.
* \item Shape groups cannot be used to replicate shapes with
* attached emitters, sensors, or subsurface scattering models.
* }
*/ */
Instance::Instance(const Properties &props) : Shape(props) { Instance::Instance(const Properties &props) : Shape(props) {
m_objectToWorld = props.getTransform("toWorld", Transform()); m_transform = props.getAnimatedTransform("toWorld", Transform());
m_worldToObject = m_objectToWorld.inverse();
} }
Instance::Instance(Stream *stream, InstanceManager *manager) Instance::Instance(Stream *stream, InstanceManager *manager)
: Shape(stream, manager) { : Shape(stream, manager) {
m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream)); m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream));
m_objectToWorld = Transform(stream); m_transform = new AnimatedTransform(stream);
m_worldToObject = m_objectToWorld.inverse();
} }
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_transform->serialize(stream);
} }
void Instance::configure() { void Instance::configure() {
@ -64,15 +80,19 @@ AABB Instance::getAABB() const {
const AABB &aabb = kdtree->getAABB(); const AABB &aabb = kdtree->getAABB();
if (!aabb.isValid()) // the geometry group is empty if (!aabb.isValid()) // the geometry group is empty
return aabb; return aabb;
AABB result;
for (int i=0; i<8; ++i)
result.expandBy(m_objectToWorld(aabb.getCorner(i)));
return result;
}
Float Instance::getSurfaceArea() const { std::set<Float> times;
Log(EError, "Instance::getSurfaceArea(): not supported!"); m_transform->collectKeyframes(times);
return 0.0f;
AABB result;
for (std::set<Float>::iterator it = times.begin(); it != times.end(); ++it) {
const Transform &trafo = m_transform->eval(*it);
for (int i=0; i<8; ++i)
result.expandBy(trafo(aabb.getCorner(i)));
}
return result;
} }
void Instance::addChild(const std::string &name, ConfigurableObject *child) { void Instance::addChild(const std::string &name, ConfigurableObject *child) {
@ -95,55 +115,61 @@ size_t Instance::getEffectivePrimitiveCount() const {
bool Instance::rayIntersect(const Ray &_ray, Float mint, bool Instance::rayIntersect(const Ray &_ray, Float mint,
Float maxt, Float &t, void *temp) const { Float maxt, Float &t, void *temp) const {
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree(); const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
const Transform &trafo = m_transform->eval(_ray.time);
Ray ray; Ray ray;
m_worldToObject(_ray, ray); trafo.inverse()(_ray, ray);
return kdtree->rayIntersect(ray, mint, maxt, t, temp); return kdtree->rayIntersect(ray, mint, maxt, t, temp);
} }
bool Instance::rayIntersect(const Ray &_ray, Float mint, Float maxt) const { bool Instance::rayIntersect(const Ray &_ray, Float mint, Float maxt) const {
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree(); const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
Ray ray; Ray ray;
m_worldToObject(_ray, ray); const Transform &trafo = m_transform->eval(_ray.time);
trafo.inverse()(_ray, ray);
return kdtree->rayIntersect(ray, mint, maxt); return kdtree->rayIntersect(ray, mint, maxt);
} }
void Instance::fillIntersectionRecord(const Ray &_ray, void Instance::fillIntersectionRecord(const Ray &_ray,
const void *temp, Intersection &its) const { const void *temp, Intersection &its) const {
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree(); const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
const Transform &trafo = m_transform->eval(_ray.time);
Ray ray; Ray ray;
m_worldToObject(_ray, ray); trafo.inverse()(_ray, ray);
kdtree->fillIntersectionRecord<false>(ray, temp, its); kdtree->fillIntersectionRecord<false>(ray, temp, its);
its.shFrame.n = normalize(m_objectToWorld(its.shFrame.n)); its.shFrame.n = normalize(trafo(its.shFrame.n));
its.shFrame.s = normalize(m_objectToWorld(its.shFrame.s)); its.shFrame.s = normalize(trafo(its.shFrame.s));
its.shFrame.t = normalize(m_objectToWorld(its.shFrame.t)); its.shFrame.t = normalize(trafo(its.shFrame.t));
its.geoFrame = Frame(normalize(m_objectToWorld(its.geoFrame.n))); its.geoFrame = Frame(normalize(trafo(its.geoFrame.n)));
its.dpdu = m_objectToWorld(its.dpdu); its.dpdu = trafo(its.dpdu);
its.dpdv = m_objectToWorld(its.dpdv); its.dpdv = trafo(its.dpdv);
its.p = m_objectToWorld(its.p); its.p = trafo(its.p);
its.wi = normalize(its.shFrame.toLocal(-_ray.d)); its.wi = normalize(its.shFrame.toLocal(-_ray.d));
its.instance = this; its.instance = this;
} }
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 {
const Transform &trafo = m_transform->eval(its.time);
const Transform invTrafo = trafo.inverse();
/* The following is really super-inefficient, but it's /* The following is really super-inefficient, but it's
needed to be able to deal with general transformations */ needed to be able to deal with general transformations */
Intersection temp(its); Intersection temp(its);
temp.p = m_worldToObject(its.p); temp.p = invTrafo(its.p);
temp.dpdu = m_worldToObject(its.dpdu); temp.dpdu = invTrafo(its.dpdu);
temp.dpdv = m_worldToObject(its.dpdv); temp.dpdv = invTrafo(its.dpdv);
/* Determine the length of the transformed normal /* Determine the length of the transformed normal
*before* it was re-normalized */ *before* it was re-normalized */
Normal tn = m_objectToWorld(normalize(m_worldToObject(its.shFrame.n))); Normal tn = trafo(normalize(invTrafo(its.shFrame.n)));
Float invLen = 1/tn.length(); Float invLen = 1 / tn.length();
tn *= invLen; tn *= invLen;
its.shape->getNormalDerivative(temp, dndu, dndv, shadingFrame); its.shape->getNormalDerivative(temp, dndu, dndv, shadingFrame);
dndu = m_objectToWorld(Normal(dndu)) * invLen; dndu = trafo(Normal(dndu)) * invLen;
dndv = m_objectToWorld(Normal(dndv)) * invLen; dndv = trafo(Normal(dndv)) * invLen;
dndu -= tn * dot(tn, dndu); dndu -= tn * dot(tn, dndu);
dndv -= tn * dot(tn, dndv); dndv -= tn * dot(tn, dndv);

View File

@ -40,7 +40,7 @@ public:
void configure(); void configure();
/// Return the object-to-world transformation used by this instance /// Return the object-to-world transformation used by this instance
inline Transform getWorldTransform() const { return m_objectToWorld; } inline const AnimatedTransform *getWorldTransform() const { return m_transform.get(); }
/// Add a child ConfigurableObject /// Add a child ConfigurableObject
void addChild(const std::string &name, ConfigurableObject *child); void addChild(const std::string &name, ConfigurableObject *child);
@ -57,8 +57,6 @@ public:
AABB getAABB() const; AABB getAABB() const;
Float getSurfaceArea() const;
bool rayIntersect(const Ray &_ray, Float mint, bool rayIntersect(const Ray &_ray, Float mint,
Float maxt, Float &t, void *temp) const; Float maxt, Float &t, void *temp) const;
@ -80,7 +78,7 @@ public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
ref<ShapeGroup> m_shapeGroup; ref<ShapeGroup> m_shapeGroup;
Transform m_objectToWorld, m_worldToObject; ref<const AnimatedTransform> m_transform;
}; };
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -55,7 +55,7 @@ MTS_NAMESPACE_BEGIN
* this convention. \default{\code{true}, i.e. flip them to get the * this convention. \default{\code{true}, i.e. flip them to get the
* correct coordinates}. * correct coordinates}.
* } * }
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional linear object-to-world transformation. * Specifies an optional linear object-to-world transformation.
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}

View File

@ -62,7 +62,7 @@ MTS_NAMESPACE_BEGIN
* Optional flag to flip all normals. \default{\code{false}, i.e. * Optional flag to flip all normals. \default{\code{false}, i.e.
* the normals are left unchanged}. * the normals are left unchanged}.
* } * }
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional linear object-to-world transformation. * Specifies an optional linear object-to-world transformation.
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}
@ -108,13 +108,14 @@ public:
Log(EError, "PLY file \"%s\" could not be found!", filePath.string().c_str()); Log(EError, "PLY file \"%s\" could not be found!", filePath.string().c_str());
m_triangleCount = m_vertexCount = 0; m_triangleCount = m_vertexCount = 0;
m_vertexCtr = m_triangleCtr = m_triangleIdxCtr = 0; m_vertexCtr = m_faceCount = m_faceCtr = m_indexCtr = 0;
m_normal = Normal(0.0f); m_normal = Normal(0.0f);
m_uv = Point2(0.0f); m_uv = Point2(0.0f);
m_hasNormals = false; m_hasNormals = false;
m_hasTexCoords = false; m_hasTexCoords = false;
memset(&m_triangle, 0, sizeof(Triangle)); memset(&m_face, 0, sizeof(uint32_t)*4);
loadPLY(filePath); loadPLY(filePath);
if (m_triangleCount == 0 || m_vertexCount == 0) if (m_triangleCount == 0 || m_vertexCount == 0)
Log(EError, "Unable to load \"%s\" (no triangles or vertices found)!"); Log(EError, "Unable to load \"%s\" (no triangles or vertices found)!");
@ -125,7 +126,15 @@ public:
rebuildTopology(props.getFloat("maxSmoothAngle")); rebuildTopology(props.getFloat("maxSmoothAngle"));
} }
Assert(m_triangleCtr == m_triangleCount); if (m_triangleCount < m_faceCount * 2) {
/* Needed less memory than the earlier conservative estimate -- free it! */
Triangle *temp = new Triangle[m_triangleCount];
memcpy(temp, m_triangles, sizeof(Triangle) * m_triangleCount);
delete[] m_triangles;
m_triangles = temp;
}
Assert(m_faceCtr == m_faceCount);
Assert(m_vertexCtr == m_vertexCount); Assert(m_vertexCtr == m_vertexCount);
} }
@ -171,8 +180,8 @@ public:
std::tr1::bind(&PLYLoader::vertex_end_callback, this) std::tr1::bind(&PLYLoader::vertex_end_callback, this)
); );
} else if (element_name == "face") { } else if (element_name == "face") {
m_triangleCount = count; m_faceCount = count;
m_triangles = new Triangle[m_triangleCount]; m_triangles = new Triangle[m_faceCount*2];
return std::tr1::tuple<std::tr1::function<void()>, return std::tr1::tuple<std::tr1::function<void()>,
std::tr1::function<void()> >( std::tr1::function<void()> >(
std::tr1::bind(&PLYLoader::face_begin_callback, this), std::tr1::bind(&PLYLoader::face_begin_callback, this),
@ -263,32 +272,42 @@ public:
void face_end_callback() { } void face_end_callback() { }
void face_vertex_indices_begin_uint8(ply::uint8 size) { void face_vertex_indices_begin_uint8(ply::uint8 size) {
if (size != 3) if (size != 3 && size != 4)
Log(EError, "Only triangle PLY meshes are supported for now."); Log(EError, "Only triangle and quad-based PLY meshes are supported for now.");
m_triangleIdxCtr = 0; m_indexCtr = 0;
} }
void face_vertex_indices_begin_uint32(ply::uint32 size) { void face_vertex_indices_begin_uint32(ply::uint32 size) {
if (size != 3) if (size != 3 && size != 4)
Log(EError, "Only triangle PLY meshes are supported for now."); Log(EError, "Only triangle and quad-based PLY meshes are supported for now.");
m_triangleIdxCtr = 0; m_indexCtr = 0;
} }
void face_vertex_indices_element_int32(ply::int32 element) { void face_vertex_indices_element_int32(ply::int32 element) {
Assert(m_triangleIdxCtr < 3); Assert(m_indexCtr < 4);
Assert((size_t) element < m_vertexCount); Assert((size_t) element < m_vertexCount);
m_triangle.idx[m_triangleIdxCtr++] = element; m_face[m_indexCtr++] = element;
} }
void face_vertex_indices_element_uint32(ply::uint32 element) { void face_vertex_indices_element_uint32(ply::uint32 element) {
Assert(m_triangleIdxCtr < 3); Assert(m_indexCtr < 4);
Assert((size_t) element < m_vertexCount); Assert((size_t) element < m_vertexCount);
m_triangle.idx[m_triangleIdxCtr++] = element; m_face[m_indexCtr++] = element;
} }
void face_vertex_indices_end() { void face_vertex_indices_end() {
Assert(m_triangleIdxCtr == 3); Assert(m_indexCtr == 3 || m_indexCtr == 4);
m_triangles[m_triangleCtr++] = m_triangle;
Triangle t;
t.idx[0] = m_face[0]; t.idx[1] = m_face[1]; t.idx[2] = m_face[2];
m_triangles[m_triangleCount++] = t;
if (m_indexCtr == 4) {
t.idx[0] = m_face[3]; t.idx[1] = m_face[0]; t.idx[2] = m_face[2];
m_triangles[m_triangleCount++] = t;
}
m_faceCtr++;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
@ -297,8 +316,9 @@ private:
Normal m_normal; Normal m_normal;
Float m_red, m_green, m_blue; Float m_red, m_green, m_blue;
Transform m_objectToWorld; Transform m_objectToWorld;
size_t m_vertexCtr, m_triangleCtr, m_triangleIdxCtr; size_t m_faceCount, m_vertexCtr;
Triangle m_triangle; size_t m_faceCtr, m_indexCtr;
uint32_t m_face[4];
bool m_hasNormals, m_hasTexCoords; bool m_hasNormals, m_hasTexCoords;
Point2 m_uv; Point2 m_uv;
bool m_sRGB; bool m_sRGB;
@ -380,7 +400,7 @@ template<> std::tr1::tuple<std::tr1::function<void (ply::uint8)>,
std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> > std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >
PLYLoader::list_property_definition_callback(const std::string& element_name, PLYLoader::list_property_definition_callback(const std::string& element_name,
const std::string& property_name) { const std::string& property_name) {
if ((element_name == "face") && (property_name == "vertex_indices")) { if ((element_name == "face") && (property_name == "vertex_indices" || property_name == "vertex_index")) {
return std::tr1::tuple<std::tr1::function<void (ply::uint8)>, return std::tr1::tuple<std::tr1::function<void (ply::uint8)>,
std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >( std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >(
std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint8, this, _1), std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint8, this, _1),
@ -404,7 +424,7 @@ template<> std::tr1::tuple<std::tr1::function<void (ply::uint32)>,
std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> > std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >
PLYLoader::list_property_definition_callback(const std::string& element_name, PLYLoader::list_property_definition_callback(const std::string& element_name,
const std::string& property_name) { const std::string& property_name) {
if ((element_name == "face") && (property_name == "vertex_indices")) { if ((element_name == "face") && (property_name == "vertex_indices" || property_name == "vertex_index")) {
return std::tr1::tuple<std::tr1::function<void (ply::uint32)>, return std::tr1::tuple<std::tr1::function<void (ply::uint32)>,
std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >( std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >(
std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint32, this, _1), std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint32, this, _1),
@ -428,7 +448,7 @@ template<> std::tr1::tuple<std::tr1::function<void (ply::uint8)>,
std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> > std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >
PLYLoader::list_property_definition_callback(const std::string& element_name, PLYLoader::list_property_definition_callback(const std::string& element_name,
const std::string& property_name) { const std::string& property_name) {
if ((element_name == "face") && (property_name == "vertex_indices")) { if ((element_name == "face") && (property_name == "vertex_indices" || property_name == "vertex_index")) {
return std::tr1::tuple<std::tr1::function<void (ply::uint8)>, return std::tr1::tuple<std::tr1::function<void (ply::uint8)>,
std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >( std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >(
std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint8, this, _1), std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint8, this, _1),
@ -452,7 +472,7 @@ template<> std::tr1::tuple<std::tr1::function<void (ply::uint32)>,
std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> > std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >
PLYLoader::list_property_definition_callback(const std::string& element_name, PLYLoader::list_property_definition_callback(const std::string& element_name,
const std::string& property_name) { const std::string& property_name) {
if ((element_name == "face") && (property_name == "vertex_indices")) { if ((element_name == "face") && (property_name == "vertex_indices" || property_name == "vertex_index")) {
return std::tr1::tuple<std::tr1::function<void (ply::uint32)>, return std::tr1::tuple<std::tr1::function<void (ply::uint32)>,
std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >( std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >(
std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint32, this, _1), std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint32, this, _1),

View File

@ -30,7 +30,7 @@ MTS_NAMESPACE_BEGIN
/*!\plugin{rectangle}{Rectangle intersection primitive} /*!\plugin{rectangle}{Rectangle intersection primitive}
* \order{3} * \order{3}
* \parameters{ * \parameters{
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies a linear object-to-world transformation. * Specifies a linear object-to-world transformation.
* It is allowed to use non-uniform scaling, but no shear. * It is allowed to use non-uniform scaling, but no shear.
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}

View File

@ -55,7 +55,7 @@ MTS_NAMESPACE_BEGIN
* Optional flag to flip all normals. \default{\code{false}, i.e. * Optional flag to flip all normals. \default{\code{false}, i.e.
* the normals are left unchanged}. * the normals are left unchanged}.
* } * }
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional linear object-to-world transformation. * Specifies an optional linear object-to-world transformation.
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}

View File

@ -34,9 +34,9 @@ MTS_NAMESPACE_BEGIN
* so that they can efficiently be referenced many times using the * so that they can efficiently be referenced many times using the
* \pluginref{instance} plugin. This is useful for rendering things like * \pluginref{instance} plugin. This is useful for rendering things like
* forests, where only a few distinct types of trees have to be kept * forests, where only a few distinct types of trees have to be kept
* in memory. * in memory. An example is given below:
* *
* \vspace{5mm}
* \begin{xml}[caption={An example of geometry instancing}, label=lst:instancing] * \begin{xml}[caption={An example of geometry instancing}, label=lst:instancing]
* <!-- Declare a named shape group containing two objects --> * <!-- Declare a named shape group containing two objects -->
* <shape type="shapegroup" id="myShapeGroup"> * <shape type="shapegroup" id="myShapeGroup">
@ -68,14 +68,6 @@ MTS_NAMESPACE_BEGIN
* </transform> * </transform>
* </shape> * </shape>
* \end{xml} * \end{xml}
* \vspace{-2mm}
* \remarks{
* \item Note that it is not possible to assign a different
* material to each instance --- the material assignment specified within
* the shape group is the one that matters.
* \item Shape groups can not be used to replicate shapes with
* attached emitters, sensors, or subsurface scattering models.
* }
*/ */
ShapeGroup::ShapeGroup(const Properties &props) : Shape(props) { ShapeGroup::ShapeGroup(const Properties &props) : Shape(props) {
@ -120,8 +112,8 @@ Float ShapeGroup::getSurfaceArea() const {
void ShapeGroup::addChild(const std::string &name, ConfigurableObject *child) { void ShapeGroup::addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass(); const Class *cClass = child->getClass();
if (cClass->derivesFrom(MTS_CLASS(ShapeGroup)) || cClass->getName() == "ShapeInstance") { if (cClass->derivesFrom(MTS_CLASS(ShapeGroup)) || cClass->getName() == "Instance") {
Log(EError, "Nested instancing is not supported!"); Log(EError, "Nested instancing is not permitted");
} else if (cClass->derivesFrom(MTS_CLASS(Shape))) { } else if (cClass->derivesFrom(MTS_CLASS(Shape))) {
Shape *shape = static_cast<Shape *>(child); Shape *shape = static_cast<Shape *>(child);
if (shape->isEmitter()) if (shape->isEmitter())

View File

@ -37,7 +37,7 @@ MTS_NAMESPACE_BEGIN
* \parameter{radius}{\Float}{ * \parameter{radius}{\Float}{
* Radius of the sphere in object-space units \default{1} * Radius of the sphere in object-space units \default{1}
* } * }
* \parameter{toWorld}{\Transform}{ * \parameter{toWorld}{\Transform\Or\Animation}{
* Specifies an optional linear object-to-world transformation. * Specifies an optional linear object-to-world transformation.
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}

View File

@ -9,7 +9,7 @@ bidirEnv.Append(LIBPATH=['#src/libbidir'])
for plugin in glob.glob(GetBuildPath('test_*.cpp')): for plugin in glob.glob(GetBuildPath('test_*.cpp')):
name = os.path.basename(plugin) name = os.path.basename(plugin)
if "bidir" in name: if "bidir" in name:
lib = bidirEnv.SharedLibrary(name[0:len(name)-4], name) lib = bidirEnv.SharedLibrary(name[0:len(name)-4], name)
else: else:
lib = testEnv.SharedLibrary(name[0:len(name)-4], name) lib = testEnv.SharedLibrary(name[0:len(name)-4], name)