merged motion blur branch, updated version
commit
874a392336
|
@ -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']
|
||||
BASEINCLUDE = ['#include', '#dependencies/include']
|
||||
BASELIBDIR = ['#dependencies/lib']
|
||||
BASELIB = ['m', 'pthread', 'gomp', 'Half']
|
||||
BASELIB = ['m', 'pthread', 'Half']
|
||||
OEXRINCLUDE = ['#dependencies/include/OpenEXR']
|
||||
OEXRLIB = ['IlmImf', 'Imath', 'Iex', 'z']
|
||||
PNGLIB = ['png']
|
||||
|
|
|
@ -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/applications
|
||||
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/*/*
|
||||
cp dist/libmitsuba-*.so $RPM_BUILD_ROOT%{_libdir}
|
||||
cp dist/mtsgui $RPM_BUILD_ROOT%{_bindir}
|
||||
cp dist/mitsuba $RPM_BUILD_ROOT%{_bindir}
|
||||
cp dist/mtssrv $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/plugins/* $RPM_BUILD_ROOT/usr/share/mitsuba/plugins
|
||||
cp -Rdp dist/data $RPM_BUILD_ROOT/usr/share/mitsuba/data
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<xsd:element name="rgb" type="rgb"/>
|
||||
<xsd:element name="srgb" type="string"/>
|
||||
<xsd:element name="blackbody" type="blackbody"/>
|
||||
|
||||
|
||||
<xsd:element name="alias" type="alias"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="version" type="xsd:string"/>
|
||||
|
@ -43,6 +43,7 @@
|
|||
<xsd:element name="vector" type="point"/>
|
||||
<xsd:element name="boolean" type="boolean"/>
|
||||
<xsd:element name="transform" type="transform"/>
|
||||
<xsd:element name="atransform" type="atransform"/>
|
||||
<xsd:element name="string" type="string"/>
|
||||
<xsd:element name="spectrum" type="spectrum"/>
|
||||
<xsd:element name="rgb" type="rgb"/>
|
||||
|
@ -50,14 +51,14 @@
|
|||
<xsd:element name="blackbody" type="blackbody"/>
|
||||
</xsd:choice>
|
||||
</xsd:group>
|
||||
|
||||
|
||||
<xsd:complexType name="objectBase">
|
||||
<xsd:attribute name="type" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="id" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
|
||||
<xsd:complexType name="object">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="objectBase">
|
||||
|
@ -140,7 +141,7 @@
|
|||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<!-- MEDIUM Element -->
|
||||
<xsd:complexType name="medium">
|
||||
<xsd:complexContent>
|
||||
|
@ -297,7 +298,7 @@
|
|||
<xsd:complexType name="include">
|
||||
<xsd:attribute name="filename" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<xsd:complexType name="alias">
|
||||
<xsd:attribute name="id" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="as" type="xsd:string" use="required"/>
|
||||
|
@ -314,6 +315,23 @@
|
|||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
</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="atransform">
|
||||
<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:attribute name="x" type="doubleType"/>
|
||||
<xsd:attribute name="y" type="doubleType"/>
|
||||
|
|
|
@ -276,7 +276,7 @@ choices are available:
|
|||
\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"/>
|
||||
\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
|
||||
(optional) \code{up} parameter determines the ``upward'' direction in the final rendered image.
|
||||
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}
|
||||
Cordinates that are zero (for \code{translate} and \code{rotate}) or one (for \code{scale})
|
||||
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}
|
||||
<atransform 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) ..
|
||||
</atransform>
|
||||
\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
|
||||
to declare it over and over again, which wastes memory, you can make use of references. Here is an example
|
||||
of how this works:
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
\newcommand{\order}[1]{} % Ignore
|
||||
|
||||
\newcommand{\Transform}{\texttt{transform}}
|
||||
\newcommand{\Animation}{\texttt{animation}}
|
||||
\newcommand{\ATransform}{\texttt{atransform}}
|
||||
\newcommand{\Spectrum}{\texttt{spectrum}}
|
||||
\newcommand{\Integer}{\texttt{integer}}
|
||||
\newcommand{\String}{\texttt{string}}
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
medium,film,sampler,integrator,emitter,sensor,
|
||||
translate,rotate,scale,lookat,point,vector,matrix,
|
||||
include,fscat,volume,alias,rfilter,boolean,
|
||||
subsurface
|
||||
subsurface,atransform
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -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_ */
|
|
@ -59,6 +59,8 @@ public:
|
|||
EVector,
|
||||
/// 4x4 transform for homogeneous coordinates
|
||||
ETransform,
|
||||
/// An animated 4x4 transformation
|
||||
EAnimatedTransform,
|
||||
/// Discretized color spectrum
|
||||
ESpectrum,
|
||||
/// Arbitrary-length string
|
||||
|
@ -152,6 +154,15 @@ public:
|
|||
/// Get a linear transformation (with default)
|
||||
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
|
||||
void setSpectrum(const std::string &name, const Spectrum &value, bool warnDuplicates = true);
|
||||
/// Get a spectral power distribution
|
||||
|
|
|
@ -132,12 +132,17 @@ template <typename T> struct TQuaternion {
|
|||
|
||||
/// Equality test
|
||||
bool operator==(const TQuaternion &q) const {
|
||||
return v == q.v && v.w == q.w;
|
||||
return v == q.v && w == q.w;
|
||||
}
|
||||
|
||||
/// Inequality test
|
||||
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
|
||||
|
@ -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
|
||||
* rotation matrix.
|
||||
*/
|
||||
static TQuaternion fromTransform(const Transform trafo) {
|
||||
static TQuaternion fromMatrix(const Matrix4x4 &m) {
|
||||
/// Implementation from PBRT
|
||||
const Matrix4x4 &m = trafo.getMatrix();
|
||||
T trace = m.m[0][0] + m.m[1][1] + m.m[2][2];
|
||||
T trace = m(0, 0) + m(1, 1) + m(2, 2);
|
||||
TVector3<T> v; T w;
|
||||
if (trace > 0.f) {
|
||||
// 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);
|
||||
w = s / 2.0f;
|
||||
s = 0.5f / s;
|
||||
v.x = (m.m[2][1] - m.m[1][2]) * s;
|
||||
v.y = (m.m[0][2] - m.m[2][0]) * s;
|
||||
v.z = (m.m[1][0] - m.m[0][1]) * s;
|
||||
v.x = (m(2, 1) - m(1, 2)) * s;
|
||||
v.y = (m(0, 2) - m(2, 0)) * s;
|
||||
v.z = (m(1, 0) - m(0, 1)) * s;
|
||||
} else {
|
||||
// Compute largest of $x$, $y$, or $z$, then remaining components
|
||||
const int nxt[3] = {1, 2, 0};
|
||||
T q[3];
|
||||
int i = 0;
|
||||
if (m.m[1][1] > m.m[0][0]) i = 1;
|
||||
if (m.m[2][2] > m.m[i][i]) i = 2;
|
||||
if (m(1, 1) > m(0, 0)) i = 1;
|
||||
if (m(2, 2) > m(i, i)) i = 2;
|
||||
int j = nxt[i];
|
||||
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;
|
||||
if (s != 0.f) s = 0.5f / s;
|
||||
w = (m.m[k][j] - m.m[j][k]) * s;
|
||||
q[j] = (m.m[j][i] + m.m[i][j]) * s;
|
||||
q[k] = (m.m[k][i] + m.m[i][k]) * s;
|
||||
w = (m(k, j) - m(j, k)) * s;
|
||||
q[j] = (m(j, i) + m(i, j)) * s;
|
||||
q[k] = (m(k, i) + m(i, k)) * s;
|
||||
v.x = q[0];
|
||||
v.y = q[1];
|
||||
v.z = q[2];
|
||||
|
|
|
@ -20,120 +20,5 @@
|
|||
#if !defined(__MITSUBA_CORE_STL_H_)
|
||||
#define __MITSUBA_CORE_STL_H_
|
||||
|
||||
namespace mitsuba {
|
||||
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
|
||||
/// \endcond
|
||||
#endif /* __MITSUBA_CORE_STL_H_ */
|
||||
|
|
|
@ -26,13 +26,13 @@ MTS_NAMESPACE_BEGIN
|
|||
* \brief Current release of Mitsuba
|
||||
* \ingroup libcore
|
||||
*/
|
||||
#define MTS_VERSION "0.4.2"
|
||||
#define MTS_VERSION "0.4.3"
|
||||
|
||||
/**
|
||||
* \brief Year of the current release
|
||||
* \ingroup libcore
|
||||
*/
|
||||
#define MTS_YEAR "2012"
|
||||
#define MTS_YEAR "2013"
|
||||
|
||||
/**
|
||||
* \brief A simple data structure for representing and
|
||||
|
|
|
@ -326,11 +326,14 @@ protected:
|
|||
* number of GPU pipeline flushes. Draw transparent objects last.
|
||||
*/
|
||||
struct MaterialOrder {
|
||||
inline bool operator()(
|
||||
const Renderer::TransformedGPUGeometry &g1,
|
||||
const Renderer::TransformedGPUGeometry &g2) const {
|
||||
const Shader *shader1 = g1.first->getShader();
|
||||
const Shader *shader2 = g2.first->getShader();
|
||||
const std::vector<Renderer::TransformedGPUGeometry> &geo;
|
||||
|
||||
MaterialOrder(const std::vector<Renderer::TransformedGPUGeometry> &geo)
|
||||
: geo(geo) { }
|
||||
|
||||
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))
|
||||
shader1 = NULL;
|
||||
|
@ -340,6 +343,19 @@ protected:
|
|||
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()
|
||||
private:
|
||||
ref<Renderer> m_renderer;
|
||||
|
@ -348,6 +364,7 @@ private:
|
|||
/* On-GPU geometry references */
|
||||
std::vector<Renderer::TransformedGPUGeometry> m_geometry;
|
||||
std::vector<Renderer::TransformedGPUGeometry> m_opaqueGeometry;
|
||||
std::vector<AnimatedGeometryRecord> m_animatedGeometry;
|
||||
|
||||
/* Shader & dependency management */
|
||||
std::map<std::string, VPLConfiguration> m_configurations;
|
||||
|
|
|
@ -46,6 +46,7 @@ using std::endl;
|
|||
#include <mitsuba/core/fwd.h>
|
||||
#include <mitsuba/render/fwd.h>
|
||||
#include <mitsuba/core/stl.h>
|
||||
#include <mitsuba/core/math.h>
|
||||
#include <mitsuba/core/object.h>
|
||||
#include <mitsuba/core/ref.h>
|
||||
#include <mitsuba/core/tls.h>
|
||||
|
|
|
@ -425,7 +425,7 @@ protected:
|
|||
/// Virtual destructor
|
||||
virtual ~AbstractEmitter();
|
||||
protected:
|
||||
ref<AnimatedTransform> m_worldTransform;
|
||||
ref<const AnimatedTransform> m_worldTransform;
|
||||
ref<Medium> m_medium;
|
||||
Shape *m_shape;
|
||||
uint32_t m_type;
|
||||
|
|
|
@ -1321,7 +1321,9 @@ protected:
|
|||
return a.axis < b.axis;
|
||||
if (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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@ public:
|
|||
* 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.
|
||||
* 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 {
|
||||
return std::pair<Float, Float>(
|
||||
|
|
|
@ -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 */
|
|
@ -158,7 +158,8 @@ private:
|
|||
EBoolean, EString, ETranslate, ERotate,
|
||||
ELookAt, EScale, EMatrix, EPoint,
|
||||
EVector, ERGB, ESRGB, EBlackBody,
|
||||
ESpectrum, ETransform, EInclude, EAlias
|
||||
ESpectrum, ETransform, EATransform,
|
||||
EInclude, EAlias
|
||||
};
|
||||
|
||||
typedef std::pair<ETag, const Class *> TagEntry;
|
||||
|
@ -173,6 +174,7 @@ private:
|
|||
std::stack<ParseContext> m_context;
|
||||
TagMap m_tags;
|
||||
Transform m_transform;
|
||||
ref<AnimatedTransform> m_animatedTransform;
|
||||
bool m_isIncludedFile;
|
||||
};
|
||||
|
||||
|
|
|
@ -193,6 +193,9 @@ public:
|
|||
/**
|
||||
* \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
|
||||
*/
|
||||
virtual Float getSurfaceArea() const;
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
|
||||
#if defined(SINGLE_PRECISION)
|
||||
/// 32 byte temporary storage for intersection computations
|
||||
#define MTS_KD_INTERSECTION_TEMP 32
|
||||
#else
|
||||
#define MTS_KD_INTERSECTION_TEMP 64
|
||||
#else
|
||||
#define MTS_KD_INTERSECTION_TEMP 128
|
||||
#endif
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <mitsuba/core/quat.h>
|
||||
#include <mitsuba/core/simplecache.h>
|
||||
#include <set>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -65,6 +66,9 @@ public:
|
|||
/// Serialize to a binary data stream
|
||||
virtual void serialize(Stream *stream) const = 0;
|
||||
|
||||
/// Clone this track
|
||||
virtual AbstractAnimationTrack *clone() const = 0;
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
AbstractAnimationTrack(EType type, size_t nKeyframes)
|
||||
|
@ -82,9 +86,9 @@ protected:
|
|||
*/
|
||||
template <typename T> class AnimationTrack : public AbstractAnimationTrack {
|
||||
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) { }
|
||||
|
||||
AnimationTrack(EType type, Stream *stream)
|
||||
|
@ -95,11 +99,38 @@ public:
|
|||
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
|
||||
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
|
||||
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
|
||||
inline void serialize(Stream *stream) const {
|
||||
|
@ -111,7 +142,7 @@ public:
|
|||
}
|
||||
|
||||
/// 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);
|
||||
std::vector<Float>::const_iterator entry =
|
||||
std::lower_bound(m_times.begin(), m_times.end(), time);
|
||||
|
@ -126,19 +157,84 @@ public:
|
|||
}
|
||||
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:
|
||||
/// 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) {
|
||||
value = stream->readElement<value_type>();
|
||||
/// Is this a "no-op" transformation?
|
||||
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 {
|
||||
stream->writeElement<value_type>(value);
|
||||
inline void serialize(Stream *stream, const ValueType &value) const {
|
||||
stream->writeElement<ValueType>(value);
|
||||
}
|
||||
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 {
|
||||
|
@ -150,6 +246,53 @@ template<> inline Quaternion AnimationTrack<Quaternion>::lerp(size_t idx0, size_
|
|||
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) {
|
||||
value = Point(stream);
|
||||
}
|
||||
|
@ -179,7 +322,7 @@ template<> inline void AnimationTrack<Quaternion>::serialize(Stream *stream, con
|
|||
* \ingroup librender
|
||||
*/
|
||||
class MTS_EXPORT_RENDER AnimatedTransform : public Object {
|
||||
protected:
|
||||
private:
|
||||
/// Internal functor used by \ref eval() and \ref SimpleCache
|
||||
struct MTS_EXPORT_RENDER TransformFunctor {
|
||||
public:
|
||||
|
@ -204,12 +347,27 @@ public:
|
|||
/// Unserialized an animated transformation from a binary data stream
|
||||
AnimatedTransform(Stream *stream);
|
||||
|
||||
/// Copy constructor
|
||||
AnimatedTransform(const AnimatedTransform *trafo);
|
||||
|
||||
/// Return the number of associated animation tracks
|
||||
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
|
||||
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]; }
|
||||
|
||||
/// Return the used keyframes as a set
|
||||
void collectKeyframes(std::set<Float> &result) const;
|
||||
|
||||
/// Append an animation track
|
||||
void addTrack(AbstractAnimationTrack *track);
|
||||
|
||||
|
@ -221,7 +379,7 @@ public:
|
|||
* to this function.
|
||||
*/
|
||||
inline const Transform &eval(Float t) const {
|
||||
if (m_tracks.size() == 0)
|
||||
if (EXPECT_TAKEN(m_tracks.size() == 0))
|
||||
return m_transform;
|
||||
else
|
||||
return m_cache.get(TransformFunctor(m_tracks), t);
|
||||
|
@ -230,6 +388,12 @@ public:
|
|||
/// Is the animation static?
|
||||
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
|
||||
inline Point transformAffine(Float t, const Point &p) const {
|
||||
return eval(t).transformAffine(p);
|
||||
|
@ -290,6 +454,9 @@ public:
|
|||
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
|
||||
void serialize(Stream *stream) const;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \icon{emitter_collimated}
|
||||
* \order{5}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional emitter-to-world transformation.
|
||||
* \default{none (i.e. emitter space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -25,7 +25,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \icon{emitter_directional}
|
||||
* \order{4}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional emitter-to-world transformation.
|
||||
* \default{none (i.e. emitter space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -28,7 +28,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \icon{emitter_point}
|
||||
* \order{1}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional sensor-to-world transformation.
|
||||
* \default{none (i.e. sensor space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -71,6 +71,10 @@ MTS_NAMESPACE_BEGIN
|
|||
* Specifies the relative amount of samples
|
||||
* allocated to this emitter. \default{1}
|
||||
* }
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional sensor-to-world transformation.
|
||||
* \default{none (i.e. sensor space $=$ world space)}
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* \renderings{
|
||||
|
@ -376,8 +380,7 @@ public:
|
|||
bitmapData.ptr = (uint8_t *) bitmap.get();
|
||||
bitmapData.size = sizeof(Bitmap);
|
||||
props.setData("bitmap", bitmapData);
|
||||
const Transform &trafo = m_worldTransform->eval(0);
|
||||
props.setTransform("toWorld", trafo);
|
||||
props.setAnimatedTransform("toWorld", m_worldTransform);
|
||||
props.setFloat("samplingWeight", m_samplingWeight);
|
||||
Emitter *emitter = static_cast<Emitter *>(
|
||||
PluginManager::getInstance()->createObject(
|
||||
|
|
|
@ -25,7 +25,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \icon{emitter_spot}
|
||||
* \order{3}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional sensor-to-world transformation.
|
||||
* \default{none (i.e. sensor space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -221,8 +221,7 @@ public:
|
|||
bitmapData.ptr = (uint8_t *) bitmap.get();
|
||||
bitmapData.size = sizeof(Bitmap);
|
||||
props.setData("bitmap", bitmapData);
|
||||
const Transform &trafo = m_worldTransform->eval(0);
|
||||
props.setTransform("toWorld", trafo);
|
||||
props.setAnimatedTransform("toWorld", m_worldTransform);
|
||||
props.setFloat("samplingWeight", m_samplingWeight);
|
||||
Emitter *emitter = static_cast<Emitter *>(
|
||||
PluginManager::getInstance()->createObject(
|
||||
|
|
|
@ -223,7 +223,7 @@ public:
|
|||
bitmapData.ptr = (uint8_t *) bitmap.get();
|
||||
bitmapData.size = sizeof(Bitmap);
|
||||
envProps.setData("bitmap", bitmapData);
|
||||
envProps.setTransform("toWorld", trafo);
|
||||
envProps.setAnimatedTransform("toWorld", m_worldTransform);
|
||||
envProps.setFloat("samplingWeight", m_samplingWeight);
|
||||
m_envEmitter = static_cast<Emitter *>(
|
||||
PluginManager::getInstance()->createObject(
|
||||
|
|
|
@ -9,7 +9,7 @@ bidirEnv.Append(CPPDEFINES = [['MTS_BUILD_MODULE', 'MTS_MODULE_BIDIR']])
|
|||
libbidir = bidirEnv.SharedLibrary('mitsuba-bidir', [
|
||||
'common.cpp', 'rsampler.cpp', 'vertex.cpp', 'edge.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'
|
||||
])
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <mitsuba/core/properties.h>
|
||||
#include <mitsuba/core/netobject.h>
|
||||
#include <mitsuba/render/track.h>
|
||||
|
||||
/* Keep the boost::variant includes outside of properties.h,
|
||||
since they noticeably add to the overall compile times */
|
||||
|
@ -26,7 +27,7 @@
|
|||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
typedef boost::variant<
|
||||
bool, int64_t, Float, Point, Vector, Transform,
|
||||
bool, int64_t, Float, Point, Vector, Transform, AnimatedTransform *,
|
||||
Spectrum, std::string, Properties::Data> ElementData;
|
||||
|
||||
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(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 <atransform> 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 <atransform> 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 <atransform> 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 {
|
||||
class TypeVisitor : public boost::static_visitor<Properties::EPropertyType> {
|
||||
public:
|
||||
Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; }
|
||||
Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; }
|
||||
Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; }
|
||||
Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; }
|
||||
Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; }
|
||||
Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; }
|
||||
Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; }
|
||||
Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; }
|
||||
Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; }
|
||||
Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; }
|
||||
Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; }
|
||||
Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; }
|
||||
Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; }
|
||||
Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; }
|
||||
Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; }
|
||||
Properties::EPropertyType operator()(const AnimatedTransform *) const { return Properties::EAnimatedTransform; }
|
||||
Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; }
|
||||
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> {
|
||||
public:
|
||||
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 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 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 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 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; }
|
||||
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 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 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 AnimatedTransform *v) const { AnimatedTransform * const *v2 = boost::get<AnimatedTransform*>(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 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:
|
||||
const ElementData *ref;
|
||||
};
|
||||
|
@ -113,15 +185,16 @@ namespace {
|
|||
public:
|
||||
StringVisitor(std::ostringstream &oss, bool quote) : oss(oss), quote(quote) { }
|
||||
|
||||
void operator()(const bool &v) const { oss << (v ? "true" : "false"); }
|
||||
void operator()(const int64_t &v) const { oss << v; }
|
||||
void operator()(const Float &v) const { oss << v; }
|
||||
void operator()(const Point &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 Spectrum &v) const { oss << v.toString(); }
|
||||
void operator()(const std::string &v) const { oss << (quote ? "\"" : "") << v << (quote ? "\"" : ""); }
|
||||
void operator()(const Properties::Data &v) const { oss << v.ptr << " (size=" << v.size << ")"; }
|
||||
void operator()(const bool &v) const { oss << (v ? "true" : "false"); }
|
||||
void operator()(const int64_t &v) const { oss << v; }
|
||||
void operator()(const Float &v) const { oss << v; }
|
||||
void operator()(const Point &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 AnimatedTransform *v) const { oss << ((Object *) v)->toString(); }
|
||||
void operator()(const Spectrum &v) const { oss << v.toString(); }
|
||||
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:
|
||||
std::ostringstream &oss;
|
||||
bool quote;
|
||||
|
@ -141,16 +214,44 @@ Properties::Properties(const std::string &pluginName)
|
|||
Properties::Properties(const Properties &props)
|
||||
: m_pluginName(props.m_pluginName), m_id(props.m_id) {
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
|
||||
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_id = props.m_id;
|
||||
*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 {
|
||||
|
@ -161,6 +262,9 @@ bool Properties::removeProperty(const std::string &name) {
|
|||
std::map<std::string, PropertyElement>::iterator it = m_elements->find(name);
|
||||
if (it == m_elements->end())
|
||||
return false;
|
||||
AnimatedTransform **trafo = boost::get<AnimatedTransform *>(&(*it).second.data);
|
||||
if (trafo)
|
||||
(*trafo)->decRef();
|
||||
m_elements->erase(it);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
Import('env', 'sys', 'os')
|
||||
|
||||
libhw_objects = [
|
||||
'session.cpp', 'device.cpp', 'gputexture.cpp', 'gpugeometry.cpp',
|
||||
'gpuprogram.cpp', 'renderer.cpp', 'glrenderer.cpp', 'glprogram.cpp',
|
||||
'session.cpp', 'device.cpp', 'gputexture.cpp', 'gpugeometry.cpp',
|
||||
'gpuprogram.cpp', 'renderer.cpp', 'glrenderer.cpp', 'glprogram.cpp',
|
||||
'glgeometry.cpp', 'gltexture.cpp', 'gpusync.cpp', 'glsync.cpp',
|
||||
'vpl.cpp', 'font.cpp', 'viewer.cpp', 'basicshader.cpp', 'shadow.cpp']
|
||||
|
||||
|
@ -14,7 +14,7 @@ if sys.platform == 'win32':
|
|||
elif sys.platform == 'linux2':
|
||||
libhw_objects += ['x11session.cpp',
|
||||
'x11device.cpp',
|
||||
'glxdevice.cpp',
|
||||
'glxdevice.cpp',
|
||||
'glxrenderer.cpp']
|
||||
|
||||
glEnv = env.Clone()
|
||||
|
|
|
@ -146,6 +146,7 @@ void VPLShaderManager::setScene(const Scene *scene) {
|
|||
m_geometry.reserve(shapes.size());
|
||||
m_opaqueGeometry.clear();
|
||||
m_opaqueGeometry.reserve(shapes.size());
|
||||
m_animatedGeometry.clear();
|
||||
|
||||
Matrix4x4 identityTrafo;
|
||||
identityTrafo.setIdentity();
|
||||
|
@ -158,7 +159,8 @@ void VPLShaderManager::setScene(const Scene *scene) {
|
|||
const Instance *instance = static_cast<const Instance *>(shape);
|
||||
const std::vector<const Shape *> &instantiatedShapes =
|
||||
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) {
|
||||
shape = instantiatedShapes[j];
|
||||
|
@ -173,10 +175,18 @@ void VPLShaderManager::setScene(const Scene *scene) {
|
|||
}
|
||||
|
||||
gpuGeo->setShader(shader);
|
||||
ssize_t geometryIndex = (ssize_t) m_geometry.size(), opaqueGeometryIndex = -1;
|
||||
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));
|
||||
}
|
||||
|
||||
if (!atrafo->isStatic()) {
|
||||
m_animatedGeometry.push_back(AnimatedGeometryRecord(atrafo,
|
||||
geometryIndex, opaqueGeometryIndex));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GPUGeometry *gpuGeo = m_renderer->registerGeometry(shape);
|
||||
|
@ -233,8 +243,40 @@ void VPLShaderManager::setScene(const Scene *scene) {
|
|||
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
|
||||
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) {
|
||||
|
@ -244,6 +286,18 @@ void VPLShaderManager::setVPL(const VPL &vpl) {
|
|||
m_nearClip = 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) {
|
||||
/* Trace a few rays from the VPL to estimate a suitable depth range */
|
||||
for (size_t i=0; i<sampleCount; ++i) {
|
||||
|
|
|
@ -11,12 +11,12 @@ if renderEnv.has_key('XERCESLIB'):
|
|||
|
||||
librender = renderEnv.SharedLibrary('mitsuba-render', [
|
||||
'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',
|
||||
'renderqueue.cpp', 'scene.cpp', 'subsurface.cpp', 'texture.cpp',
|
||||
'shape.cpp', 'trimesh.cpp', 'sampler.cpp', 'util.cpp', 'irrcache.cpp',
|
||||
'testcase.cpp', 'photonmap.cpp', 'gatherproc.cpp', 'volume.cpp',
|
||||
'vpl.cpp', 'shader.cpp', 'scenehandler.cpp', 'intersection.cpp',
|
||||
'renderqueue.cpp', 'scene.cpp', 'subsurface.cpp', 'texture.cpp',
|
||||
'shape.cpp', 'trimesh.cpp', 'sampler.cpp', 'util.cpp', 'irrcache.cpp',
|
||||
'testcase.cpp', 'photonmap.cpp', 'gatherproc.cpp', 'volume.cpp',
|
||||
'vpl.cpp', 'shader.cpp', 'scenehandler.cpp', 'intersection.cpp',
|
||||
'track.cpp', 'common.cpp', 'phase.cpp', 'noise.cpp', 'photon.cpp'
|
||||
])
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@ MTS_NAMESPACE_BEGIN
|
|||
|
||||
AbstractEmitter::AbstractEmitter(const Properties &props)
|
||||
: ConfigurableObject(props), m_shape(NULL), m_type(0) {
|
||||
m_worldTransform = new AnimatedTransform(
|
||||
props.getTransform("toWorld", Transform()));
|
||||
m_worldTransform = props.getAnimatedTransform("toWorld", Transform());
|
||||
}
|
||||
|
||||
AbstractEmitter::AbstractEmitter(Stream *stream, InstanceManager *manager)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <mitsuba/core/fresolver.h>
|
||||
#include <mitsuba/render/scene.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <Eigen/SVD>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
@ -102,6 +103,7 @@ SceneHandler::SceneHandler(const SAXParser *parser,
|
|||
m_tags["blackbody"] = TagEntry(EBlackBody, (Class *) NULL);
|
||||
m_tags["spectrum"] = TagEntry(ESpectrum, (Class *) NULL);
|
||||
m_tags["transform"] = TagEntry(ETransform, (Class *) NULL);
|
||||
m_tags["atransform"] = TagEntry(EATransform, (Class *) NULL);
|
||||
m_tags["include"] = TagEntry(EInclude, (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,
|
||||
const std::string &str, Float defVal) const {
|
||||
char *end_ptr = NULL;
|
||||
if (str == "") {
|
||||
if (str.empty()) {
|
||||
if (defVal == -1)
|
||||
XMLLog(EError, "Missing floating point value (in <%s>)", name.c_str());
|
||||
return defVal;
|
||||
}
|
||||
|
||||
Float result = (Float) std::strtod(str.c_str(), &end_ptr);
|
||||
if (*end_ptr != '\0')
|
||||
XMLLog(EError, "Invalid floating point value specified (in <%s>)", name.c_str());
|
||||
|
@ -251,6 +254,19 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
|
|||
case ETransform:
|
||||
m_transform = Transform();
|
||||
break;
|
||||
case EATransform: {
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
@ -270,7 +286,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
|
|||
if (context.attributes.find("id") != context.attributes.end())
|
||||
context.properties.setID(context.attributes["id"]);
|
||||
|
||||
ref<ConfigurableObject> object = NULL;
|
||||
ref<ConfigurableObject> object;
|
||||
|
||||
TagMap::const_iterator it = m_tags.find(name);
|
||||
if (it == m_tags.end())
|
||||
|
@ -590,9 +606,56 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
|
|||
}
|
||||
break;
|
||||
|
||||
case EATransform: {
|
||||
m_animatedTransform->sortAndSimplify();
|
||||
context.parent->properties.setAnimatedTransform(
|
||||
context.attributes["name"], m_animatedTransform);
|
||||
m_animatedTransform = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case ETransform: {
|
||||
context.parent->properties.setTransform(
|
||||
context.attributes["name"], m_transform);
|
||||
if (!m_animatedTransform.get()) {
|
||||
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;
|
||||
|
||||
|
@ -638,11 +701,61 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
|
|||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (tag.second == NULL)
|
||||
XMLLog(EError, "Internal error: could not instantiate an object "
|
||||
"corresponding to the tag '%s'", name.c_str());
|
||||
object = m_pluginManager->createObject(tag.second, context.properties);
|
||||
default: {
|
||||
if (tag.second == NULL)
|
||||
XMLLog(EError, "Internal error: could not instantiate an object "
|
||||
"corresponding to the tag '%s'", name.c_str());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,16 @@
|
|||
|
||||
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) {
|
||||
size_t nTracks = stream->readSize();
|
||||
if (nTracks == 0) {
|
||||
|
@ -48,18 +58,9 @@ void AnimatedTransform::addTrack(AbstractAnimationTrack *track) {
|
|||
|
||||
AABB1 AnimatedTransform::getTimeBounds() const {
|
||||
if (m_tracks.size() == 0)
|
||||
#if !defined(__clang__)
|
||||
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();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
#if !defined(__clang__)
|
||||
return AABB1(min, max);
|
||||
#else
|
||||
// HACK Workaround for clang
|
||||
AABB1 b;
|
||||
b.min = min;
|
||||
b.max = max;
|
||||
return b;
|
||||
#endif
|
||||
}
|
||||
|
||||
AABB AnimatedTransform::getTranslationBounds() const {
|
||||
|
@ -152,6 +145,122 @@ AnimatedTransform::~AnimatedTransform() {
|
|||
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 {
|
||||
stream->writeSize(m_tracks.size());
|
||||
if (m_tracks.size() == 0) {
|
||||
|
@ -203,9 +312,13 @@ void AnimatedTransform::TransformFunctor::operator()(const Float &t, Transform &
|
|||
}
|
||||
}
|
||||
|
||||
trafo = Transform::translate(translation) *
|
||||
rotation.toTransform() *
|
||||
Transform::scale(scale);
|
||||
trafo = Transform::translate(translation);
|
||||
|
||||
if (!rotation.isIdentity())
|
||||
trafo = trafo * rotation.toTransform();
|
||||
|
||||
if (scale != Vector(0.0f))
|
||||
trafo = trafo * Transform::scale(scale);
|
||||
}
|
||||
|
||||
std::string AnimatedTransform::toString() const {
|
||||
|
|
|
@ -30,7 +30,7 @@ static void appendVPL(const Scene *scene, Random *random,
|
|||
|
||||
const Sensor *sensor = scene->getSensor();
|
||||
Float time = sensor->getShutterOpen()
|
||||
+ 0.5f * sensor->getShutterOpenTime();
|
||||
+ sensor->getShutterOpenTime() * random->nextFloat();
|
||||
|
||||
if (prune) {
|
||||
/* 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()->
|
||||
createObject(MTS_CLASS(Sampler), props));
|
||||
sampler->configure();
|
||||
sampler->generate(Point2i(0));
|
||||
}
|
||||
|
||||
const Sensor *sensor = scene->getSensor();
|
||||
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));
|
||||
|
||||
|
@ -110,6 +111,7 @@ size_t generateVPLs(const Scene *scene, Random *random,
|
|||
if (!emitter->isEnvironmentEmitter() && emitter->needsDirectionSample()) {
|
||||
VPL lumVPL(EPointEmitterVPL, weight);
|
||||
lumVPL.its.p = pRec.p;
|
||||
lumVPL.its.time = time;
|
||||
lumVPL.its.shFrame = pRec.n.isZero() ? stdFrame : Frame(pRec.n);
|
||||
lumVPL.emitter = emitter;
|
||||
appendVPL(scene, random, lumVPL, prune, vpls);
|
||||
|
@ -128,6 +130,7 @@ size_t generateVPLs(const Scene *scene, Random *random,
|
|||
|
||||
VPL lumVPL(EDirectionalEmitterVPL, weight2);
|
||||
lumVPL.its.p = Point(0.0);
|
||||
lumVPL.its.time = time;
|
||||
lumVPL.its.shFrame = Frame(-diRec.d);
|
||||
lumVPL.emitter = emitter;
|
||||
appendVPL(scene, random, lumVPL, false, vpls);
|
||||
|
|
|
@ -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')
|
||||
|
||||
# For running Uic & Moc (below)
|
||||
|
@ -58,7 +58,7 @@ if hasQt:
|
|||
qtEnv.Prepend(LIBPATH=env['COLLADALIBDIR'])
|
||||
if env.has_key('COLLADALIB'):
|
||||
qtEnv.Prepend(LIBS=env['COLLADALIB'])
|
||||
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
mainEnv_osx = mainEnv.Clone()
|
||||
qtEnv_osx = qtEnv.Clone()
|
||||
|
|
|
@ -78,6 +78,31 @@ static void setProperties(QDomDocument &doc, QDomElement &element,
|
|||
property = doc.createElement("string");
|
||||
property.setAttribute("value", props.getString(*it).c_str());
|
||||
break;
|
||||
case Properties::EAnimatedTransform: {
|
||||
const AnimatedTransform *trafo = props.getAnimatedTransform(*it);
|
||||
|
||||
std::set<Float> times;
|
||||
trafo->collectKeyframes(times);
|
||||
|
||||
property = doc.createElement("atransform");
|
||||
|
||||
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: {
|
||||
/* Captures the subset of transformations that are used by
|
||||
Mitsuba's perspective and orthographic camera classes */
|
||||
|
|
|
@ -26,7 +26,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{fluencemeter}{Fluence meter}
|
||||
* \order{7}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional sensor-to-world transformation.
|
||||
* \default{none (i.e. sensor space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -27,7 +27,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{orthographic}{Orthographic camera}
|
||||
* \order{3}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional camera-to-world transformation.
|
||||
* \default{none (i.e. camera space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -26,7 +26,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{perspective}{Perspective pinhole camera}
|
||||
* \order{1}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional camera-to-world transformation.
|
||||
* \default{none (i.e. camera space $=$ world space)}
|
||||
* }
|
||||
|
@ -113,7 +113,7 @@ public:
|
|||
foreshortening terms caused by the aperture, hence the flag "EOnSurface" */
|
||||
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 "
|
||||
"transformation are not allowed!");
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{radiancemeter}{Radiance meter}
|
||||
* \order{6}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional sensor-to-world transformation.
|
||||
* \default{none (i.e. sensor space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -25,7 +25,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{spherical}{Spherical camera}
|
||||
* \order{5}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional camera-to-world transformation.
|
||||
* \default{none (i.e. camera space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -27,7 +27,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{telecentric}{Telecentric lens camera}
|
||||
* \order{4}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional sensor-to-world transformation.
|
||||
* \default{none (i.e. camera space $=$ world space)}
|
||||
* }
|
||||
|
|
|
@ -29,7 +29,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{thinlens}{Perspective camera with a thin lens}
|
||||
* \order{2}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform\Or\Animation}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional camera-to-world transformation.
|
||||
* \default{none (i.e. camera space $=$ world space)}
|
||||
* }
|
||||
|
@ -137,7 +137,7 @@ public:
|
|||
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 "
|
||||
"transformation are not allowed!");
|
||||
}
|
||||
|
@ -520,14 +520,11 @@ public:
|
|||
}
|
||||
|
||||
ref<Shape> createShape(const Scene *scene) {
|
||||
if (!m_worldTransform->isStatic())
|
||||
Log(EError, "Bidirectional renderings involving moving "
|
||||
"perspective cameras with depth of field are currently not supported!");
|
||||
Transform trafo = m_worldTransform->eval(0) *
|
||||
Transform::scale(Vector(m_apertureRadius));
|
||||
ref<AnimatedTransform> trafo = new AnimatedTransform(m_worldTransform);
|
||||
trafo->prependScale(Vector(m_apertureRadius));
|
||||
|
||||
Properties props("disk");
|
||||
props.setTransform("toWorld", trafo);
|
||||
props.setAnimatedTransform("toWorld", trafo);
|
||||
Shape *shape = static_cast<Shape *> (PluginManager::getInstance()->
|
||||
createObject(MTS_CLASS(Shape), props));
|
||||
shape->addChild(this);
|
||||
|
|
|
@ -11,6 +11,6 @@ plugins += env.SharedLibrary('cylinder', ['cylinder.cpp'])
|
|||
plugins += env.SharedLibrary('hair', ['hair.cpp'])
|
||||
plugins += env.SharedLibrary('shapegroup', ['shapegroup.cpp'])
|
||||
plugins += env.SharedLibrary('instance', ['instance.cpp'])
|
||||
plugins += env.SharedLibrary('animatedinstance', ['animatedinstance.cpp'])
|
||||
#plugins += env.SharedLibrary('deformable', ['deformable.cpp'])
|
||||
|
||||
Export('plugins')
|
||||
|
|
|
@ -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
|
|
@ -43,7 +43,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* Is the cylinder inverted, i.e. should the normal vectors
|
||||
* be flipped? \default{\code{false}, i.e. the normals point outside}
|
||||
* }
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional linear object-to-world transformation.
|
||||
* Note that non-uniform scales are not permitted!
|
||||
* \default{none (i.e. object space $=$ world space)}
|
||||
|
|
|
@ -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
|
|
@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{disk}{Disk intersection primitive}
|
||||
* \order{4}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies a linear object-to-world transformation.
|
||||
* Note that non-uniform scales are not permitted!
|
||||
* \default{none (i.e. object space $=$ world space)}
|
||||
|
@ -81,31 +81,29 @@ MTS_NAMESPACE_BEGIN
|
|||
class Disk : public Shape {
|
||||
public:
|
||||
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))
|
||||
m_objectToWorld = m_objectToWorld * Transform::scale(Vector(1, 1, -1));
|
||||
m_worldToObject = m_objectToWorld.inverse();
|
||||
m_objectToWorld->prependScale(Vector(1, 1, -1));
|
||||
}
|
||||
|
||||
Disk(Stream *stream, InstanceManager *manager)
|
||||
: Shape(stream, manager) {
|
||||
m_objectToWorld = Transform(stream);
|
||||
m_worldToObject = m_objectToWorld.inverse();
|
||||
m_objectToWorld = new AnimatedTransform(stream);
|
||||
configure();
|
||||
}
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||
Shape::serialize(stream, manager);
|
||||
m_objectToWorld.serialize(stream);
|
||||
m_objectToWorld->serialize(stream);
|
||||
}
|
||||
|
||||
void configure() {
|
||||
Shape::configure();
|
||||
|
||||
m_normal = normalize(m_objectToWorld(Normal(0, 0, 1)));
|
||||
|
||||
Vector dpdu = m_objectToWorld(Vector(1, 0, 0));
|
||||
Vector dpdv = m_objectToWorld(Vector(0, 1, 0));
|
||||
const Transform &trafo = m_objectToWorld->eval(0);
|
||||
Vector dpdu = trafo(Vector(1, 0, 0));
|
||||
Vector dpdv = trafo(Vector(0, 1, 0));
|
||||
|
||||
if (std::abs(dot(dpdu, dpdv)) > 1e-3f)
|
||||
Log(EError, "Error: 'toWorld' transformation contains shear!");
|
||||
|
@ -117,23 +115,30 @@ public:
|
|||
}
|
||||
|
||||
AABB getAABB() const {
|
||||
std::set<Float> times;
|
||||
m_objectToWorld->collectKeyframes(times);
|
||||
|
||||
AABB aabb;
|
||||
aabb.expandBy(m_objectToWorld(Point( 1, 0, 0)));
|
||||
aabb.expandBy(m_objectToWorld(Point(-1, 0, 0)));
|
||||
aabb.expandBy(m_objectToWorld(Point( 0, 1, 0)));
|
||||
aabb.expandBy(m_objectToWorld(Point( 0, -1, 0)));
|
||||
for (std::set<Float>::iterator it = times.begin(); it != times.end(); ++it) {
|
||||
const Transform &trafo = m_objectToWorld->eval(*it);
|
||||
aabb.expandBy(trafo(Point( 1, 0, 0)));
|
||||
aabb.expandBy(trafo(Point(-1, 0, 0)));
|
||||
aabb.expandBy(trafo(Point( 0, 1, 0)));
|
||||
aabb.expandBy(trafo(Point( 0, -1, 0)));
|
||||
}
|
||||
return aabb;
|
||||
}
|
||||
|
||||
Float getSurfaceArea() const {
|
||||
Vector dpdu = m_objectToWorld(Vector(1, 0, 0));
|
||||
Vector dpdv = m_objectToWorld(Vector(0, 1, 0));
|
||||
const Transform &trafo = m_objectToWorld->eval(0);
|
||||
Vector dpdu = trafo(Vector(1, 0, 0));
|
||||
Vector dpdv = trafo(Vector(0, 1, 0));
|
||||
return M_PI * dpdu.length() * dpdv.length();
|
||||
}
|
||||
|
||||
inline bool rayIntersect(const Ray &_ray, Float mint, Float maxt, Float &t, void *temp) const {
|
||||
Ray ray;
|
||||
m_worldToObject.transformAffine(_ray, ray);
|
||||
m_objectToWorld->eval(ray.time).inverse().transformAffine(_ray, ray);
|
||||
Float hit = -ray.o.z / ray.d.z;
|
||||
|
||||
if (!(hit >= mint && hit <= maxt))
|
||||
|
@ -173,18 +178,20 @@ public:
|
|||
phi += 2*M_PI;
|
||||
|
||||
Float cosPhi = data[0] * invR, sinPhi = data[1] * invR;
|
||||
const Transform &trafo = m_objectToWorld->eval(ray.time);
|
||||
|
||||
its.shape = this;
|
||||
if (r != 0) {
|
||||
its.dpdu = m_objectToWorld(Vector(cosPhi, sinPhi, 0));
|
||||
its.dpdv = m_objectToWorld(Vector(-sinPhi, cosPhi, 0));
|
||||
its.dpdu = trafo(Vector(cosPhi, sinPhi, 0));
|
||||
its.dpdv = trafo(Vector(-sinPhi, cosPhi, 0));
|
||||
} else {
|
||||
its.dpdu = m_objectToWorld(Vector(1, 0, 0));
|
||||
its.dpdv = m_objectToWorld(Vector(0, 1, 0));
|
||||
its.dpdu = trafo(Vector(1, 0, 0));
|
||||
its.dpdv = trafo(Vector(0, 1, 0));
|
||||
}
|
||||
|
||||
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.p = ray(its.t);
|
||||
its.wi = its.toLocal(-ray.d);
|
||||
|
@ -206,16 +213,19 @@ public:
|
|||
|
||||
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) {
|
||||
Float phi = i*dphi;
|
||||
vertices[i] = center;
|
||||
vertices[phiSteps+i] = m_objectToWorld(
|
||||
vertices[phiSteps+i] = trafo(
|
||||
Point(std::cos(phi), std::sin(phi), 0)
|
||||
);
|
||||
|
||||
normals[i] = m_normal;
|
||||
normals[phiSteps+i] = m_normal;
|
||||
normals[i] = normal;
|
||||
normals[phiSteps+i] = normal;
|
||||
texcoords[i] = Point2(0.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 {
|
||||
const Transform &trafo = m_objectToWorld->eval(pRec.time);
|
||||
Point2 p = Warp::squareToUniformDiskConcentric(sample);
|
||||
|
||||
pRec.p = m_objectToWorld(Point3(p.x, p.y, 0));
|
||||
pRec.n = m_normal;
|
||||
pRec.p = trafo(Point3(p.x, p.y, 0));
|
||||
pRec.n = trafo(normalize(Normal(0,0,1)));
|
||||
pRec.pdf = m_invSurfaceArea;
|
||||
pRec.measure = EArea;
|
||||
}
|
||||
|
@ -261,7 +272,7 @@ public:
|
|||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "Disk[" << endl
|
||||
<< " objectToWorld = " << indent(m_objectToWorld.toString()) << "," << endl
|
||||
<< " objectToWorld = " << indent(m_objectToWorld->toString()) << "," << endl
|
||||
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl;
|
||||
if (isMediumTransition()) {
|
||||
oss << " interiorMedium = " << indent(m_interiorMedium.toString()) << "," << endl
|
||||
|
@ -276,10 +287,7 @@ public:
|
|||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
Transform m_objectToWorld;
|
||||
Transform m_worldToObject;
|
||||
Normal m_normal;
|
||||
Float m_surfaceArea;
|
||||
ref<AnimatedTransform> m_objectToWorld;
|
||||
Float m_invSurfaceArea;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \parameters{
|
||||
* \parameter{\Unnamed}{\ShapeGroup}{A reference to a
|
||||
* shape group that should be instantiated}
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional linear instance-to-world transformation.
|
||||
* \default{none (i.e. instance space $=$ world space)}
|
||||
* }
|
||||
|
@ -37,21 +37,19 @@ MTS_NAMESPACE_BEGIN
|
|||
*/
|
||||
|
||||
Instance::Instance(const Properties &props) : Shape(props) {
|
||||
m_objectToWorld = props.getTransform("toWorld", Transform());
|
||||
m_worldToObject = m_objectToWorld.inverse();
|
||||
m_transform = props.getAnimatedTransform("toWorld", Transform());
|
||||
}
|
||||
|
||||
Instance::Instance(Stream *stream, InstanceManager *manager)
|
||||
: Shape(stream, manager) {
|
||||
m_shapeGroup = static_cast<ShapeGroup *>(manager->getInstance(stream));
|
||||
m_objectToWorld = Transform(stream);
|
||||
m_worldToObject = m_objectToWorld.inverse();
|
||||
m_transform = new AnimatedTransform(stream);
|
||||
}
|
||||
|
||||
void Instance::serialize(Stream *stream, InstanceManager *manager) const {
|
||||
Shape::serialize(stream, manager);
|
||||
manager->serialize(stream, m_shapeGroup.get());
|
||||
m_objectToWorld.serialize(stream);
|
||||
m_transform->serialize(stream);
|
||||
}
|
||||
|
||||
void Instance::configure() {
|
||||
|
@ -64,15 +62,19 @@ AABB Instance::getAABB() const {
|
|||
const AABB &aabb = kdtree->getAABB();
|
||||
if (!aabb.isValid()) // the geometry group is empty
|
||||
return aabb;
|
||||
AABB result;
|
||||
for (int i=0; i<8; ++i)
|
||||
result.expandBy(m_objectToWorld(aabb.getCorner(i)));
|
||||
return result;
|
||||
}
|
||||
|
||||
Float Instance::getSurfaceArea() const {
|
||||
Log(EError, "Instance::getSurfaceArea(): not supported!");
|
||||
return 0.0f;
|
||||
std::set<Float> times;
|
||||
m_transform->collectKeyframes(times);
|
||||
|
||||
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) {
|
||||
|
@ -95,55 +97,61 @@ size_t Instance::getEffectivePrimitiveCount() const {
|
|||
bool Instance::rayIntersect(const Ray &_ray, Float mint,
|
||||
Float maxt, Float &t, void *temp) const {
|
||||
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
|
||||
const Transform &trafo = m_transform->eval(_ray.time);
|
||||
Ray ray;
|
||||
m_worldToObject(_ray, ray);
|
||||
trafo.inverse()(_ray, ray);
|
||||
return kdtree->rayIntersect(ray, mint, maxt, t, temp);
|
||||
}
|
||||
|
||||
bool Instance::rayIntersect(const Ray &_ray, Float mint, Float maxt) const {
|
||||
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
|
||||
Ray ray;
|
||||
m_worldToObject(_ray, ray);
|
||||
const Transform &trafo = m_transform->eval(_ray.time);
|
||||
trafo.inverse()(_ray, ray);
|
||||
return kdtree->rayIntersect(ray, mint, maxt);
|
||||
}
|
||||
|
||||
void Instance::fillIntersectionRecord(const Ray &_ray,
|
||||
const void *temp, Intersection &its) const {
|
||||
const ShapeKDTree *kdtree = m_shapeGroup->getKDTree();
|
||||
const Transform &trafo = m_transform->eval(_ray.time);
|
||||
Ray ray;
|
||||
m_worldToObject(_ray, ray);
|
||||
trafo.inverse()(_ray, ray);
|
||||
kdtree->fillIntersectionRecord<false>(ray, temp, its);
|
||||
|
||||
its.shFrame.n = normalize(m_objectToWorld(its.shFrame.n));
|
||||
its.shFrame.s = normalize(m_objectToWorld(its.shFrame.s));
|
||||
its.shFrame.t = normalize(m_objectToWorld(its.shFrame.t));
|
||||
its.geoFrame = Frame(normalize(m_objectToWorld(its.geoFrame.n)));
|
||||
its.dpdu = m_objectToWorld(its.dpdu);
|
||||
its.dpdv = m_objectToWorld(its.dpdv);
|
||||
its.p = m_objectToWorld(its.p);
|
||||
its.shFrame.n = normalize(trafo(its.shFrame.n));
|
||||
its.shFrame.s = normalize(trafo(its.shFrame.s));
|
||||
its.shFrame.t = normalize(trafo(its.shFrame.t));
|
||||
its.geoFrame = Frame(normalize(trafo(its.geoFrame.n)));
|
||||
its.dpdu = trafo(its.dpdu);
|
||||
its.dpdv = trafo(its.dpdv);
|
||||
its.p = trafo(its.p);
|
||||
its.wi = normalize(its.shFrame.toLocal(-_ray.d));
|
||||
its.instance = this;
|
||||
}
|
||||
|
||||
void Instance::getNormalDerivative(const Intersection &its,
|
||||
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
|
||||
needed to be able to deal with general transformations */
|
||||
Intersection temp(its);
|
||||
temp.p = m_worldToObject(its.p);
|
||||
temp.dpdu = m_worldToObject(its.dpdu);
|
||||
temp.dpdv = m_worldToObject(its.dpdv);
|
||||
temp.p = invTrafo(its.p);
|
||||
temp.dpdu = invTrafo(its.dpdu);
|
||||
temp.dpdv = invTrafo(its.dpdv);
|
||||
|
||||
/* Determine the length of the transformed normal
|
||||
*before* it was re-normalized */
|
||||
Normal tn = m_objectToWorld(normalize(m_worldToObject(its.shFrame.n)));
|
||||
Float invLen = 1/tn.length();
|
||||
Normal tn = trafo(normalize(invTrafo(its.shFrame.n)));
|
||||
Float invLen = 1 / tn.length();
|
||||
tn *= invLen;
|
||||
|
||||
its.shape->getNormalDerivative(temp, dndu, dndv, shadingFrame);
|
||||
|
||||
dndu = m_objectToWorld(Normal(dndu)) * invLen;
|
||||
dndv = m_objectToWorld(Normal(dndv)) * invLen;
|
||||
dndu = trafo(Normal(dndu)) * invLen;
|
||||
dndv = trafo(Normal(dndv)) * invLen;
|
||||
|
||||
dndu -= tn * dot(tn, dndu);
|
||||
dndv -= tn * dot(tn, dndv);
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
void configure();
|
||||
|
||||
/// 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
|
||||
void addChild(const std::string &name, ConfigurableObject *child);
|
||||
|
@ -57,8 +57,6 @@ public:
|
|||
|
||||
AABB getAABB() const;
|
||||
|
||||
Float getSurfaceArea() const;
|
||||
|
||||
bool rayIntersect(const Ray &_ray, Float mint,
|
||||
Float maxt, Float &t, void *temp) const;
|
||||
|
||||
|
@ -80,7 +78,7 @@ public:
|
|||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
ref<ShapeGroup> m_shapeGroup;
|
||||
Transform m_objectToWorld, m_worldToObject;
|
||||
ref<const AnimatedTransform> m_transform;
|
||||
};
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -55,7 +55,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* this convention. \default{\code{true}, i.e. flip them to get the
|
||||
* correct coordinates}.
|
||||
* }
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional linear object-to-world transformation.
|
||||
* Note that non-uniform scales are not permitted!
|
||||
* \default{none (i.e. object space $=$ world space)}
|
||||
|
|
|
@ -62,7 +62,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* Optional flag to flip all normals. \default{\code{false}, i.e.
|
||||
* the normals are left unchanged}.
|
||||
* }
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional linear object-to-world transformation.
|
||||
* Note that non-uniform scales are not permitted!
|
||||
* \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());
|
||||
|
||||
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_uv = Point2(0.0f);
|
||||
m_hasNormals = false;
|
||||
m_hasTexCoords = false;
|
||||
memset(&m_triangle, 0, sizeof(Triangle));
|
||||
memset(&m_face, 0, sizeof(uint32_t)*4);
|
||||
loadPLY(filePath);
|
||||
|
||||
if (m_triangleCount == 0 || m_vertexCount == 0)
|
||||
Log(EError, "Unable to load \"%s\" (no triangles or vertices found)!");
|
||||
|
||||
|
@ -125,7 +126,15 @@ public:
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -171,8 +180,8 @@ public:
|
|||
std::tr1::bind(&PLYLoader::vertex_end_callback, this)
|
||||
);
|
||||
} else if (element_name == "face") {
|
||||
m_triangleCount = count;
|
||||
m_triangles = new Triangle[m_triangleCount];
|
||||
m_faceCount = count;
|
||||
m_triangles = new Triangle[m_faceCount*2];
|
||||
return std::tr1::tuple<std::tr1::function<void()>,
|
||||
std::tr1::function<void()> >(
|
||||
std::tr1::bind(&PLYLoader::face_begin_callback, this),
|
||||
|
@ -263,32 +272,42 @@ public:
|
|||
void face_end_callback() { }
|
||||
|
||||
void face_vertex_indices_begin_uint8(ply::uint8 size) {
|
||||
if (size != 3)
|
||||
Log(EError, "Only triangle PLY meshes are supported for now.");
|
||||
m_triangleIdxCtr = 0;
|
||||
if (size != 3 && size != 4)
|
||||
Log(EError, "Only triangle and quad-based PLY meshes are supported for now.");
|
||||
m_indexCtr = 0;
|
||||
}
|
||||
|
||||
void face_vertex_indices_begin_uint32(ply::uint32 size) {
|
||||
if (size != 3)
|
||||
Log(EError, "Only triangle PLY meshes are supported for now.");
|
||||
m_triangleIdxCtr = 0;
|
||||
if (size != 3 && size != 4)
|
||||
Log(EError, "Only triangle and quad-based PLY meshes are supported for now.");
|
||||
m_indexCtr = 0;
|
||||
}
|
||||
|
||||
void face_vertex_indices_element_int32(ply::int32 element) {
|
||||
Assert(m_triangleIdxCtr < 3);
|
||||
Assert(m_indexCtr < 4);
|
||||
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) {
|
||||
Assert(m_triangleIdxCtr < 3);
|
||||
Assert(m_indexCtr < 4);
|
||||
Assert((size_t) element < m_vertexCount);
|
||||
m_triangle.idx[m_triangleIdxCtr++] = element;
|
||||
m_face[m_indexCtr++] = element;
|
||||
}
|
||||
|
||||
void face_vertex_indices_end() {
|
||||
Assert(m_triangleIdxCtr == 3);
|
||||
m_triangles[m_triangleCtr++] = m_triangle;
|
||||
Assert(m_indexCtr == 3 || m_indexCtr == 4);
|
||||
|
||||
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()
|
||||
|
@ -297,8 +316,9 @@ private:
|
|||
Normal m_normal;
|
||||
Float m_red, m_green, m_blue;
|
||||
Transform m_objectToWorld;
|
||||
size_t m_vertexCtr, m_triangleCtr, m_triangleIdxCtr;
|
||||
Triangle m_triangle;
|
||||
size_t m_faceCount, m_vertexCtr;
|
||||
size_t m_faceCtr, m_indexCtr;
|
||||
uint32_t m_face[4];
|
||||
bool m_hasNormals, m_hasTexCoords;
|
||||
Point2 m_uv;
|
||||
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 ()> >
|
||||
PLYLoader::list_property_definition_callback(const std::string& element_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)>,
|
||||
std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >(
|
||||
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 ()> >
|
||||
PLYLoader::list_property_definition_callback(const std::string& element_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)>,
|
||||
std::tr1::function<void (ply::int32)>, std::tr1::function<void ()> >(
|
||||
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 ()> >
|
||||
PLYLoader::list_property_definition_callback(const std::string& element_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)>,
|
||||
std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >(
|
||||
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 ()> >
|
||||
PLYLoader::list_property_definition_callback(const std::string& element_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)>,
|
||||
std::tr1::function<void (ply::uint32)>, std::tr1::function<void ()> >(
|
||||
std::tr1::bind(&PLYLoader::face_vertex_indices_begin_uint32, this, _1),
|
||||
|
|
|
@ -30,7 +30,7 @@ MTS_NAMESPACE_BEGIN
|
|||
/*!\plugin{rectangle}{Rectangle intersection primitive}
|
||||
* \order{3}
|
||||
* \parameters{
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies a linear object-to-world transformation.
|
||||
* It is allowed to use non-uniform scaling, but no shear.
|
||||
* \default{none (i.e. object space $=$ world space)}
|
||||
|
|
|
@ -55,7 +55,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* Optional flag to flip all normals. \default{\code{false}, i.e.
|
||||
* the normals are left unchanged}.
|
||||
* }
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional linear object-to-world transformation.
|
||||
* Note that non-uniform scales are not permitted!
|
||||
* \default{none (i.e. object space $=$ world space)}
|
||||
|
|
|
@ -120,8 +120,8 @@ Float ShapeGroup::getSurfaceArea() const {
|
|||
|
||||
void ShapeGroup::addChild(const std::string &name, ConfigurableObject *child) {
|
||||
const Class *cClass = child->getClass();
|
||||
if (cClass->derivesFrom(MTS_CLASS(ShapeGroup)) || cClass->getName() == "ShapeInstance") {
|
||||
Log(EError, "Nested instancing is not supported!");
|
||||
if (cClass->derivesFrom(MTS_CLASS(ShapeGroup)) || cClass->getName() == "Instance") {
|
||||
Log(EError, "Nested instancing is not permitted");
|
||||
} else if (cClass->derivesFrom(MTS_CLASS(Shape))) {
|
||||
Shape *shape = static_cast<Shape *>(child);
|
||||
if (shape->isEmitter())
|
||||
|
|
|
@ -37,7 +37,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* \parameter{radius}{\Float}{
|
||||
* Radius of the sphere in object-space units \default{1}
|
||||
* }
|
||||
* \parameter{toWorld}{\Transform}{
|
||||
* \parameter{toWorld}{\Transform\Or\ATransform}{
|
||||
* Specifies an optional linear object-to-world transformation.
|
||||
* Note that non-uniform scales are not permitted!
|
||||
* \default{none (i.e. object space $=$ world space)}
|
||||
|
|
|
@ -9,7 +9,7 @@ bidirEnv.Append(LIBPATH=['#src/libbidir'])
|
|||
|
||||
for plugin in glob.glob(GetBuildPath('test_*.cpp')):
|
||||
name = os.path.basename(plugin)
|
||||
if "bidir" in name:
|
||||
if "bidir" in name:
|
||||
lib = bidirEnv.SharedLibrary(name[0:len(name)-4], name)
|
||||
else:
|
||||
lib = testEnv.SharedLibrary(name[0:len(name)-4], name)
|
||||
|
|
Loading…
Reference in New Issue