merged motion blur branch, updated version

metadata
Wenzel Jakob 2013-01-28 17:44:47 -05:00
commit 874a392336
61 changed files with 1870 additions and 535 deletions

View File

@ -6,7 +6,7 @@ CCFLAGS = ['-arch', 'x86_64', '-mmacosx-version-min=10.7', '-march=nocona
LINKFLAGS = ['-framework', 'OpenGL', '-framework', 'Cocoa', '-arch', 'x86_64', '-mmacosx-version-min=10.7', '-Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk', '-Wl,-headerpad,128']
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']

View File

@ -35,13 +35,14 @@ mkdir -p $RPM_BUILD_ROOT/usr/share/mitsuba/plugins
mkdir -p $RPM_BUILD_ROOT/usr/share/pixmaps
mkdir -p $RPM_BUILD_ROOT/usr/share/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

View File

@ -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"/>

View File

@ -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:

View File

@ -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}}

View File

@ -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
},
}

View File

@ -24,7 +24,7 @@ It is important to keep in mind that other applications may not support this
``linearized sRGB'' space---in particular, the Mac OS preview currently
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

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

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

View File

@ -59,6 +59,8 @@ public:
EVector,
/// 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

View File

@ -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];

View File

@ -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_ */

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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;

View File

@ -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;
}
};

View File

@ -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>(

View File

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

View File

@ -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;
};

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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)}
* }

View File

@ -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)}
* }

View File

@ -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)}
* }

View File

@ -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(

View File

@ -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)}
* }

View File

@ -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(

View File

@ -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(

View File

@ -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'
])

View File

@ -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;
}

View File

@ -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()

View File

@ -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) {

View File

@ -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'
])

View File

@ -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)

View File

@ -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;
}

View File

@ -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 {

View File

@ -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);

View File

@ -1,4 +1,4 @@
Import('env', 'os', 'glob', 'sys', 'hasQt', 'hasCollada', 'hasBreakpad', 'mainEnv',
Import('env', 'os', 'glob', 'sys', 'hasQt', 'hasCollada', 'hasBreakpad', 'mainEnv',
'resources', 'converter_objects')
# 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()

View File

@ -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 */

View File

@ -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)}
* }

View File

@ -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)}
* }

View File

@ -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!");
}

View File

@ -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)}
* }

View File

@ -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)}
* }

View File

@ -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)}
* }

View File

@ -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);

View File

@ -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')

View File

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

View File

@ -43,7 +43,7 @@ MTS_NAMESPACE_BEGIN
* Is the cylinder inverted, i.e. should the normal vectors
* 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)}

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

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

View File

@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN
/*!\plugin{disk}{Disk intersection primitive}
* \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;
};

View File

@ -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);

View File

@ -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

View File

@ -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)}

View File

@ -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),

View File

@ -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)}

View File

@ -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)}

View File

@ -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())

View File

@ -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)}

View File

@ -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)