diff --git a/build/config-macos10.7-gcc-x86_64.py b/build/config-macos10.7-gcc-x86_64.py
index b4112976..5714db52 100644
--- a/build/config-macos10.7-gcc-x86_64.py
+++ b/build/config-macos10.7-gcc-x86_64.py
@@ -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']
diff --git a/data/linux/fedora/mitsuba.spec b/data/linux/fedora/mitsuba.spec
index b1958bc3..aeb0a0f3 100644
--- a/data/linux/fedora/mitsuba.spec
+++ b/data/linux/fedora/mitsuba.spec
@@ -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
diff --git a/data/schema/scene.xsd b/data/schema/scene.xsd
index 1a073b82..18de916b 100644
--- a/data/schema/scene.xsd
+++ b/data/schema/scene.xsd
@@ -26,7 +26,7 @@
-
+
@@ -43,6 +43,7 @@
+
@@ -50,14 +51,14 @@
-
+
-
+
@@ -140,7 +141,7 @@
-
+
@@ -297,7 +298,7 @@
-
+
@@ -314,6 +315,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/format.tex b/doc/format.tex
index cd03be70..d837adde 100644
--- a/doc/format.tex
+++ b/doc/format.tex
@@ -276,7 +276,7 @@ choices are available:
\begin{xml}
\end{xml}
-\item lookat transformations --- this is primarily useful for setting up cameras (and spot lights). The \code{origin} coordinates
+\item \code{lookat} transformations --- this is primarily useful for setting up cameras (and spot lights). The \code{origin} coordinates
specify the camera origin, \code{target} is the point that the camera will look at, and the
(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}
+
+
+ .. chained list of transformations as discussed above ..
+
+
+
+ .. chained list of transformations as discussed above ..
+
+
+ .. additional transformations (optional) ..
+
+\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:
diff --git a/doc/images/shape_instance_fractal_bot.jpg b/doc/images/shape_instance_fractal_bot.jpg
new file mode 100644
index 00000000..c136c9a7
Binary files /dev/null and b/doc/images/shape_instance_fractal_bot.jpg differ
diff --git a/doc/images/shape_instance_fractal_top.jpg b/doc/images/shape_instance_fractal_top.jpg
new file mode 100644
index 00000000..e827827c
Binary files /dev/null and b/doc/images/shape_instance_fractal_top.jpg differ
diff --git a/doc/main.tex b/doc/main.tex
index 170e619d..f4b3c7a0 100644
--- a/doc/main.tex
+++ b/doc/main.tex
@@ -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,animation
},
}
diff --git a/doc/misc.tex b/doc/misc.tex
index 34cc1329..890915db 100644
--- a/doc/misc.tex
+++ b/doc/misc.tex
@@ -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
diff --git a/include/mitsuba/core/math.h b/include/mitsuba/core/math.h
new file mode 100644
index 00000000..2d01b4ab
--- /dev/null
+++ b/include/mitsuba/core/math.h
@@ -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 .
+*/
+
+#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_ */
diff --git a/include/mitsuba/core/properties.h b/include/mitsuba/core/properties.h
index 2b898836..b8728daf 100644
--- a/include/mitsuba/core/properties.h
+++ b/include/mitsuba/core/properties.h
@@ -59,6 +59,8 @@ public:
EVector,
/// 4x4 transform for homogeneous coordinates
ETransform,
+ /// An animated 4x4 transformation
+ EAnimatedTransform,
/// Discretized color spectrum
ESpectrum,
/// Arbitrary-length string
@@ -152,6 +154,15 @@ public:
/// Get a linear transformation (with default)
Transform getTransform(const std::string &name, const Transform &defVal) const;
+ /// Set an animated linear transformation
+ void setAnimatedTransform(const std::string &name, const AnimatedTransform *value, bool warnDuplicates = true);
+ /// Get an animated linear transformation
+ ref getAnimatedTransform(const std::string &name) const;
+ /// Get an animated linear transformation (with default)
+ ref getAnimatedTransform(const std::string &name, const AnimatedTransform *defVal) const;
+ /// Get an animated linear transformation (with default)
+ ref 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
@@ -185,6 +196,10 @@ public:
/// Return one of the parameters (converting it to a string if necessary, with default value)
std::string getAsString(const std::string &name, const std::string &defVal) const;
+ /// Copy an attribute from another Properties object and potentially rename it
+ void copyAttribute(const Properties &properties,
+ const std::string &sourceName, const std::string &targetName);
+
/// Store an array containing the names of all stored properties
void putPropertyNames(std::vector &results) const;
diff --git a/include/mitsuba/core/quat.h b/include/mitsuba/core/quat.h
index cb8d98ee..66067290 100644
--- a/include/mitsuba/core/quat.h
+++ b/include/mitsuba/core/quat.h
@@ -132,12 +132,17 @@ template 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 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 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];
diff --git a/include/mitsuba/core/stl.h b/include/mitsuba/core/stl.h
index f12a2a6c..05d31cef 100644
--- a/include/mitsuba/core/stl.h
+++ b/include/mitsuba/core/stl.h
@@ -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_ */
diff --git a/include/mitsuba/render/track.h b/include/mitsuba/core/track.h
similarity index 59%
rename from include/mitsuba/render/track.h
rename to include/mitsuba/core/track.h
index 25049a3d..b996ec9b 100644
--- a/include/mitsuba/render/track.h
+++ b/include/mitsuba/core/track.h
@@ -17,11 +17,12 @@
*/
#pragma once
-#if !defined(__MITSUBA_RENDER_TRACK_H_)
-#define __MITSUBA_RENDER_TRACK_H_
+#if !defined(__MITSUBA_CORE_TRACK_H_)
+#define __MITSUBA_CORE_TRACK_H_
#include
#include
+#include
MTS_NAMESPACE_BEGIN
@@ -31,7 +32,7 @@ template class AnimationTrack;
* \brief Base class of animation tracks
* \ingroup librender
*/
-class MTS_EXPORT_RENDER AbstractAnimationTrack : public Object {
+class MTS_EXPORT_CORE AbstractAnimationTrack : public Object {
template friend class AnimationTrack;
public:
enum EType {
@@ -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 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 0);
std::vector::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 &p1,
+ const std::pair &p2) const {
+ return p1.first < p2.first;
+ }
+ };
+
+ struct UniqueTimePredicate {
+ inline bool operator()(const std::pair &p1,
+ const std::pair &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 > temp(m_values.size());
+ for (size_t i=0; ireadElement();
+ /// 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();
}
- inline void serialize(Stream *stream, const value_type &value) const {
- stream->writeElement(value);
+ inline void serialize(Stream *stream, const ValueType &value) const {
+ stream->writeElement(value);
}
private:
- std::vector m_values;
+ std::vector m_values;
};
template inline T AnimationTrack::lerp(size_t idx0, size_t idx1, Float t) const {
@@ -150,6 +246,53 @@ template<> inline Quaternion AnimationTrack::lerp(size_t idx0, size_
return slerp(m_values[idx0], m_values[idx1], t);
}
+template inline T AnimationTrack::concatenateTransformations(
+ const T &value1, const T &value2) const {
+ return value1 * value2;
+}
+
+template<> inline Vector AnimationTrack::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::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 inline bool AnimationTrack::isNoOp(const ValueType &value) const {
+ return false;
+}
+
+template<> inline bool AnimationTrack::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::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::isNoOp(const Quaternion &value) const {
+ return value.isIdentity();
+}
+
template<> inline void AnimationTrack::unserialize(Stream *stream, Point &value) {
value = Point(stream);
}
@@ -178,8 +321,8 @@ template<> inline void AnimationTrack::serialize(Stream *stream, con
* \brief Animated transformation with an underlying keyframe representation
* \ingroup librender
*/
-class MTS_EXPORT_RENDER AnimatedTransform : public Object {
-protected:
+class MTS_EXPORT_CORE AnimatedTransform : public Object {
+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 &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;
@@ -317,4 +484,4 @@ private:
MTS_NAMESPACE_END
-#endif /* __MITSUBA_RENDER_TRACK_H_ */
+#endif /* __MITSUBA_CORE_TRACK_H_ */
diff --git a/include/mitsuba/core/util.h b/include/mitsuba/core/util.h
index 985afeef..7bd67cb6 100644
--- a/include/mitsuba/core/util.h
+++ b/include/mitsuba/core/util.h
@@ -110,6 +110,9 @@ extern MTS_EXPORT_CORE int getCoreCount();
/// Return the host name of this machine
extern MTS_EXPORT_CORE std::string getHostName();
+/// Return the process private memory usage in bytes
+extern MTS_EXPORT_CORE size_t getPrivateMemoryUsage();
+
/// Return the fully qualified domain name of this machine
extern MTS_EXPORT_CORE std::string getFQDN();
diff --git a/include/mitsuba/core/version.h b/include/mitsuba/core/version.h
index fbc7528e..50aeaf79 100644
--- a/include/mitsuba/core/version.h
+++ b/include/mitsuba/core/version.h
@@ -26,13 +26,13 @@ MTS_NAMESPACE_BEGIN
* \brief Current release of Mitsuba
* \ingroup libcore
*/
-#define MTS_VERSION "0.4.2"
+#define MTS_VERSION "0.4.3"
/**
* \brief Year of the current release
* \ingroup libcore
*/
-#define MTS_YEAR "2012"
+#define MTS_YEAR "2013"
/**
* \brief A simple data structure for representing and
diff --git a/include/mitsuba/hw/vpl.h b/include/mitsuba/hw/vpl.h
index f37307e2..7474bfff 100644
--- a/include/mitsuba/hw/vpl.h
+++ b/include/mitsuba/hw/vpl.h
@@ -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 &geo;
+
+ MaterialOrder(const std::vector &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 m_renderer;
@@ -348,6 +364,7 @@ private:
/* On-GPU geometry references */
std::vector m_geometry;
std::vector m_opaqueGeometry;
+ std::vector m_animatedGeometry;
/* Shader & dependency management */
std::map m_configurations;
diff --git a/include/mitsuba/mitsuba.h b/include/mitsuba/mitsuba.h
index 16a1c90c..d097e798 100644
--- a/include/mitsuba/mitsuba.h
+++ b/include/mitsuba/mitsuba.h
@@ -46,6 +46,7 @@ using std::endl;
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/mitsuba/render/emitter.h b/include/mitsuba/render/emitter.h
index b5836b03..27908b50 100644
--- a/include/mitsuba/render/emitter.h
+++ b/include/mitsuba/render/emitter.h
@@ -21,7 +21,7 @@
#define __MITSUBA_RENDER_EMITTER_H_
#include
-#include
+#include
#include
#include
#include
@@ -425,7 +425,7 @@ protected:
/// Virtual destructor
virtual ~AbstractEmitter();
protected:
- ref m_worldTransform;
+ ref m_worldTransform;
ref m_medium;
Shape *m_shape;
uint32_t m_type;
diff --git a/include/mitsuba/render/film.h b/include/mitsuba/render/film.h
index fb3361e2..72674c4e 100644
--- a/include/mitsuba/render/film.h
+++ b/include/mitsuba/render/film.h
@@ -61,7 +61,7 @@ public:
virtual void setDestinationFile(const fs::path &filename, uint32_t blockSize) = 0;
/// Develop the film and write the result to the previously specified filename
- virtual void develop() = 0;
+ virtual void develop(const Scene *scene, Float renderTime) = 0;
/**
* \brief Develop the contents of a subregion of the film and store
diff --git a/include/mitsuba/render/gkdtree.h b/include/mitsuba/render/gkdtree.h
index c0509d97..4f19ec4e 100644
--- a/include/mitsuba/render/gkdtree.h
+++ b/include/mitsuba/render/gkdtree.h
@@ -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;
}
};
diff --git a/include/mitsuba/render/renderjob.h b/include/mitsuba/render/renderjob.h
index ba870552..edc4b7f0 100644
--- a/include/mitsuba/render/renderjob.h
+++ b/include/mitsuba/render/renderjob.h
@@ -75,7 +75,7 @@ public:
bool interactive = false);
/// Write out the current (partially rendered) image
- inline void flush() { m_scene->flush(); }
+ inline void flush() { m_scene->flush(m_queue, this); }
/// Cancel a running render job
inline void cancel() { m_scene->cancel(); }
diff --git a/include/mitsuba/render/renderqueue.h b/include/mitsuba/render/renderqueue.h
index 3a9944a5..fd3ca3e4 100644
--- a/include/mitsuba/render/renderqueue.h
+++ b/include/mitsuba/render/renderqueue.h
@@ -76,6 +76,9 @@ public:
/// Remove a (finished) render job from the queue
void removeJob(RenderJob *thr, bool wasCancelled);
+ /// Return the amount of time spent rendering the given job (in seconds)
+ Float getRenderTime(const RenderJob *job) const;
+
/// Register a render listener
void registerListener(RenderListener *listener);
diff --git a/include/mitsuba/render/sahkdtree3.h b/include/mitsuba/render/sahkdtree3.h
index 9db4338d..8053d121 100644
--- a/include/mitsuba/render/sahkdtree3.h
+++ b/include/mitsuba/render/sahkdtree3.h
@@ -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 operator()(int axis, Float leftWidth, Float rightWidth) const {
return std::pair(
diff --git a/include/mitsuba/render/sahkdtree4.h b/include/mitsuba/render/sahkdtree4.h
new file mode 100644
index 00000000..6900bd80
--- /dev/null
+++ b/include/mitsuba/render/sahkdtree4.h
@@ -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 .
+*/
+
+#if !defined(__SAH_KDTREE4_H)
+#define __SAH_KDTREE4_H
+
+#include
+
+MTS_NAMESPACE_BEGIN
+
+typedef TAABB 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 operator()(int axis, Float leftWidth, Float rightWidth) const {
+ if (axis == 3 && m_temp1.w == std::numeric_limits::infinity()) {
+ return std::pair(
+ std::numeric_limits::infinity(),
+ std::numeric_limits::infinity()
+ );
+ }
+
+ return std::pair(
+ 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
+ class SAHKDTree4D : public GenericKDTree {
+public:
+ typedef typename KDTreeBase::SizeType SizeType;
+ typedef typename KDTreeBase::IndexType IndexType;
+ typedef typename KDTreeBase::KDNode KDNode;
+
+protected:
+ void buildInternal() {
+ SizeType primCount = this->cast()->getPrimitiveCount();
+ KDLog(EInfo, "Constructing a 4D SAH kd-tree (%i primitives) ..", primCount);
+ GenericKDTree::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 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 */
diff --git a/include/mitsuba/render/scene.h b/include/mitsuba/render/scene.h
index 7302e5f6..fbc68ac0 100644
--- a/include/mitsuba/render/scene.h
+++ b/include/mitsuba/render/scene.h
@@ -133,7 +133,7 @@ public:
int sceneResID, int sensorResID, int samplerResID);
/// Write out the current (partially rendered) image
- void flush();
+ void flush(RenderQueue *queue, const RenderJob *job);
/**
* \brief Cancel a running rendering job
diff --git a/include/mitsuba/render/scenehandler.h b/include/mitsuba/render/scenehandler.h
index 38ebae2e..268967f8 100644
--- a/include/mitsuba/render/scenehandler.h
+++ b/include/mitsuba/render/scenehandler.h
@@ -158,7 +158,8 @@ private:
EBoolean, EString, ETranslate, ERotate,
ELookAt, EScale, EMatrix, EPoint,
EVector, ERGB, ESRGB, EBlackBody,
- ESpectrum, ETransform, EInclude, EAlias
+ ESpectrum, ETransform, EAnimation,
+ EInclude, EAlias
};
typedef std::pair TagEntry;
@@ -173,6 +174,7 @@ private:
std::stack m_context;
TagMap m_tags;
Transform m_transform;
+ ref m_animatedTransform;
bool m_isIncludedFile;
};
diff --git a/include/mitsuba/render/shape.h b/include/mitsuba/render/shape.h
index 8ad7e0a3..723c8807 100644
--- a/include/mitsuba/render/shape.h
+++ b/include/mitsuba/render/shape.h
@@ -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;
diff --git a/include/mitsuba/render/skdtree.h b/include/mitsuba/render/skdtree.h
index efc302dd..e6f25f9d 100644
--- a/include/mitsuba/render/skdtree.h
+++ b/include/mitsuba/render/skdtree.h
@@ -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
diff --git a/src/converter/collada.cpp b/src/converter/collada.cpp
index 56409067..c67214b7 100644
--- a/src/converter/collada.cpp
+++ b/src/converter/collada.cpp
@@ -22,7 +22,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
diff --git a/src/emitters/collimated.cpp b/src/emitters/collimated.cpp
index bc55ad33..3f5f4550 100644
--- a/src/emitters/collimated.cpp
+++ b/src/emitters/collimated.cpp
@@ -18,7 +18,7 @@
#include
#include
-#include
+#include
MTS_NAMESPACE_BEGIN
diff --git a/src/emitters/point.cpp b/src/emitters/point.cpp
index 5741dbf7..e2b2c3a6 100644
--- a/src/emitters/point.cpp
+++ b/src/emitters/point.cpp
@@ -18,7 +18,7 @@
#include
#include
-#include
+#include
#include
#include
diff --git a/src/emitters/sky.cpp b/src/emitters/sky.cpp
index fe3a559b..4b15f110 100644
--- a/src/emitters/sky.cpp
+++ b/src/emitters/sky.cpp
@@ -71,6 +71,10 @@ MTS_NAMESPACE_BEGIN
* Specifies the relative amount of samples
* allocated to this emitter. \default{1}
* }
+ * \parameter{toWorld}{\Transform\Or\Animation}{
+ * Specifies an optional sensor-to-world transformation.
+ * \default{none (i.e. sensor space $=$ world space)}
+ * }
* }
*
* \renderings{
@@ -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(
PluginManager::getInstance()->createObject(
diff --git a/src/emitters/sun.cpp b/src/emitters/sun.cpp
index f09ff716..89222785 100644
--- a/src/emitters/sun.cpp
+++ b/src/emitters/sun.cpp
@@ -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(
PluginManager::getInstance()->createObject(
diff --git a/src/emitters/sunsky.cpp b/src/emitters/sunsky.cpp
index 26766552..37a3c35b 100644
--- a/src/emitters/sunsky.cpp
+++ b/src/emitters/sunsky.cpp
@@ -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(
PluginManager::getInstance()->createObject(
diff --git a/src/films/CMakeLists.txt b/src/films/CMakeLists.txt
index c9633ce8..22ff2e8e 100644
--- a/src/films/CMakeLists.txt
+++ b/src/films/CMakeLists.txt
@@ -8,8 +8,8 @@ macro(add_film)
endmacro()
add_film(mfilm mfilm.cpp)
-add_film(ldrfilm ldrfilm.cpp banner.h MTS_HW)
-add_film(hdrfilm hdrfilm.cpp banner.h MTS_HW)
+add_film(ldrfilm ldrfilm.cpp annotations.h banner.h MTS_HW)
+add_film(hdrfilm hdrfilm.cpp annotations.h banner.h MTS_HW)
if (OPENEXR_FOUND)
include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS})
diff --git a/src/films/annotations.h b/src/films/annotations.h
new file mode 100644
index 00000000..6d041e03
--- /dev/null
+++ b/src/films/annotations.h
@@ -0,0 +1,137 @@
+/*
+ This file is part of Mitsuba, a physically based rendering system.
+
+ Copyright (c) 2007-2012 by Wenzel Jakob and others.
+
+ Mitsuba is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License Version 3
+ as published by the Free Software Foundation.
+
+ Mitsuba is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#pragma once
+#if !defined(__ANNOTATIONS_H)
+#define __ANNOTATIONS_H
+
+#include
+#include
+
+MTS_NAMESPACE_BEGIN
+
+/**
+ * This function implements a parser for the 'label[]' and 'metadata[]'
+ * annotations supported by the ldrfilm and hdrfilm plugins
+ */
+void annotate(const Scene *scene, const Properties &properties,
+ Bitmap *bitmap, Float renderTime, Float gamma) {
+ /* Attach the custom annotations */
+ Properties &metadata = bitmap->getMetadata();
+ std::vector keys = properties.getPropertyNames();
+ ref font;
+
+ for (size_t i=0; i args = tokenize(key.substr(6, key.length()-7), " ,");
+ if (args.size() != 2)
+ SLog(EError, "Label command '%s' has an invalid number of arguments!", key.c_str());
+ char *end_ptr = NULL;
+ offset.x = strtol(args[0].c_str(), &end_ptr, 10);
+ if (*end_ptr != '\0')
+ SLog(EError, "Label command '%s' has an invalid position argument!", key.c_str());
+ offset.y = strtol(args[1].c_str(), &end_ptr, 10);
+ if (*end_ptr != '\0')
+ SLog(EError, "Label command '%s' has an invalid position argument!", key.c_str());
+ labelAnnotation = true;
+
+ if (font == NULL) {
+ font = new Font(Font::EBitstreamVeraMono14);
+ font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), gamma);
+ }
+ } else {
+ continue;
+ }
+
+ Properties::EPropertyType type = properties.getType(keys[i]);
+ if (type == Properties::EString) {
+ std::string value = properties.getString(keys[i]);
+
+ while (true) {
+ char *strt;
+ if (!(strt = strchr((char *) value.c_str(), '$')))
+ break;
+
+ if (strncasecmp(strt, "$rendertime", 11) == 0) {
+ value.replace(strt-value.c_str(), 11, timeString(renderTime));
+ } else if (strncasecmp(strt, "$memusage", 11) == 0) {
+ value.replace(strt-value.c_str(), 11, memString(getPrivateMemoryUsage()));
+ } else {
+ char *par1, *par2;
+ if (!(par1 = strchr(strt, '[')))
+ break;
+ if (!(par2 = strchr(par1, ']')))
+ break;
+
+ std::string propSource = value.substr(strt-value.c_str()+1, par1-strt-1);
+ std::string propKey = value.substr(par1-value.c_str()+1, par2-par1-1);
+ propSource.erase(std::remove_if(propSource.begin(), propSource.end(), ::isspace), propSource.end());
+ propKey.erase(std::remove_if(propKey.begin(), propKey.end(), ::isspace), propKey.end());
+
+ if (!boost::starts_with(propKey, "'") || !boost::ends_with(propKey, "'"))
+ SLog(EError, "Encountered invalid key '%s'", propKey.c_str());
+
+ propKey = propKey.substr(1, propKey.length()-2);
+
+ const ConfigurableObject *source = NULL;
+ if (propSource == "film")
+ source = scene->getFilm();
+ else if (propSource == "sampler")
+ source = scene->getSampler();
+ else if (propSource == "sensor")
+ source = scene->getSensor();
+ else if (propSource == "integrator")
+ source = scene->getIntegrator();
+ else
+ SLog(EError, "Unknown data source '%s' (must be film/sampler/sensor/integrator)", propSource.c_str());
+
+ std::string replacement;
+ if (propKey == "type")
+ replacement = source->getProperties().getPluginName();
+ else
+ replacement = source->getProperties().getAsString(propKey);
+
+ value.replace(strt-value.c_str(), par2-strt+1, replacement);
+ }
+ }
+ if (labelAnnotation) {
+ Vector2i size = font->getSize(value);
+ bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
+ font->drawText(bitmap, offset, value);
+ } else {
+ metadata.setString(key, value);
+ }
+ } else {
+ if (labelAnnotation)
+ SLog(EError, "Only string-valued label annotations are supported!");
+ metadata.copyAttribute(properties, keys[i], key);
+ }
+ }
+}
+
+MTS_NAMESPACE_END
+
+#endif /* __ANNOTATIONS_H */
diff --git a/src/films/hdrfilm.cpp b/src/films/hdrfilm.cpp
index 57034d88..8bb01a3c 100644
--- a/src/films/hdrfilm.cpp
+++ b/src/films/hdrfilm.cpp
@@ -20,9 +20,9 @@
#include
#include
#include
-#include
#include
#include "banner.h"
+#include "annotations.h"
MTS_NAMESPACE_BEGIN
@@ -235,20 +235,11 @@ public:
std::vector keys = props.getPropertyNames();
for (size_t i=0; i args = tokenize(key.substr(5, key.length()-6), " ,");
-
- if (args.size() != 2)
- Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str());
-
- Annotation annotation;
- annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str()));
- annotation.text = props.getString(keys[i]);
- m_annotations.push_back(annotation);
- }
+ if ((boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) ||
+ (boost::starts_with(key, "text(") && boost::ends_with(key, ")")))
+ props.markQueried(keys[i]);
}
m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize);
@@ -350,7 +341,7 @@ public:
m_destFile = destFile;
}
- void develop() {
+ void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film ..");
ref bitmap = m_storage->getBitmap()->convert(
@@ -368,23 +359,6 @@ public:
}
}
- if (!m_annotations.empty()) {
- ref font = new Font(Font::EBitstreamVeraMono14);
- font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), 1.0f);
-
- for (size_t i=0; igetSize(text);
- bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
- font->drawText(bitmap, offset, text);
- }
- }
-
- for (std::map::const_iterator it = m_tags.begin();
- it != m_tags.end(); ++it)
- bitmap->setMetadataString(it->first, it->second);
-
fs::path filename = m_destFile;
std::string extension = boost::to_lower_copy(filename.extension().string());
std::string properExtension = (m_fileFormat == Bitmap::EOpenEXR) ? ".exr" : ".rgbe";
@@ -394,6 +368,8 @@ public:
Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str());
ref stream = new FileStream(filename, FileStream::ETruncWrite);
+ annotate(scene, m_properties, bitmap, renderTime, 1.0f);
+
/* Attach the log file to the image if this is requested */
Logger *logger = Thread::getThread()->getLogger();
std::string log;
@@ -439,11 +415,6 @@ public:
MTS_DECLARE_CLASS()
protected:
- struct Annotation {
- Point2i offset;
- std::string text;
- };
-
Bitmap::EFileFormat m_fileFormat;
Bitmap::EPixelFormat m_pixelFormat;
Bitmap::EComponentFormat m_componentFormat;
@@ -451,9 +422,6 @@ protected:
bool m_attachLog;
fs::path m_destFile;
ref m_storage;
-
- std::vector m_annotations;
- std::map m_tags;
};
MTS_IMPLEMENT_CLASS_S(HDRFilm, false, Film)
diff --git a/src/films/ldrfilm.cpp b/src/films/ldrfilm.cpp
index ea187fba..55ad5cab 100644
--- a/src/films/ldrfilm.cpp
+++ b/src/films/ldrfilm.cpp
@@ -20,9 +20,9 @@
#include
#include
#include
-#include
#include
#include "banner.h"
+#include "annotations.h"
MTS_NAMESPACE_BEGIN
@@ -185,20 +185,11 @@ public:
std::vector keys = props.getPropertyNames();
for (size_t i=0; i args = tokenize(key.substr(5, key.length()-6), " ,");
-
- if (args.size() != 2)
- Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str());
-
- Annotation annotation;
- annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str()));
- annotation.text = props.getString(keys[i]);
- m_annotations.push_back(annotation);
- }
+ if ((boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) ||
+ (boost::starts_with(key, "text(") && boost::ends_with(key, ")")))
+ props.markQueried(keys[i]);
}
m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize);
@@ -306,7 +297,7 @@ public:
m_destFile = destFile;
}
- void develop() {
+ void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film ..");
ref bitmap = m_storage->getBitmap();
@@ -338,23 +329,6 @@ public:
}
}
- if (!m_annotations.empty()) {
- ref font = new Font(Font::EBitstreamVeraMono14);
- font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), m_gamma);
-
- for (size_t i=0; igetSize(text);
- bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
- font->drawText(bitmap, offset, text);
- }
- }
-
- for (std::map::const_iterator it = m_tags.begin();
- it != m_tags.end(); ++it)
- bitmap->setMetadataString(it->first, it->second);
-
fs::path filename = m_destFile;
std::string extension = boost::to_lower_copy(filename.extension().string());
std::string expectedExtension;
@@ -370,6 +344,8 @@ public:
Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str());
ref stream = new FileStream(filename, FileStream::ETruncWrite);
+ annotate(scene, m_properties, bitmap, renderTime, m_gamma);
+
bitmap->write(m_fileFormat, stream);
}
@@ -415,11 +391,6 @@ public:
MTS_DECLARE_CLASS()
protected:
- struct Annotation {
- Point2i offset;
- std::string text;
- };
-
Bitmap::EFileFormat m_fileFormat;
Bitmap::EPixelFormat m_pixelFormat;
bool m_hasBanner;
@@ -428,9 +399,6 @@ protected:
ref m_storage;
ETonemapMethod m_tonemapMethod;
Float m_exposure, m_reinhardKey, m_reinhardBurn;
-
- std::vector m_annotations;
- std::map m_tags;
};
MTS_IMPLEMENT_CLASS_S(LDRFilm, false, Film)
diff --git a/src/films/mfilm.cpp b/src/films/mfilm.cpp
index 7b0792e6..3f786e69 100644
--- a/src/films/mfilm.cpp
+++ b/src/films/mfilm.cpp
@@ -238,7 +238,7 @@ public:
m_destFile = destFile;
}
- void develop() {
+ void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film ..");
fs::path filename = m_destFile;
diff --git a/src/films/tiledhdrfilm.cpp b/src/films/tiledhdrfilm.cpp
index 6666a7ee..5476168c 100644
--- a/src/films/tiledhdrfilm.cpp
+++ b/src/films/tiledhdrfilm.cpp
@@ -156,7 +156,7 @@ public:
}
virtual ~TiledHDRFilm() {
- develop();
+ develop(NULL, 0);
}
void serialize(Stream *stream, InstanceManager *manager) const {
@@ -167,7 +167,7 @@ public:
void setDestinationFile(const fs::path &destFile, uint32_t blockSize) {
if (m_output)
- develop();
+ develop(NULL, 0);
Bitmap::EPixelFormat pixelFormat = m_pixelFormat;
#if SPECTRUM_SAMPLES == 3
@@ -436,7 +436,7 @@ public:
return false; /* Not supported by the tiled EXR film! */
}
- void develop() {
+ void develop(const Scene *scene, Float renderTime) {
if (m_output) {
Log(EInfo, "Closing EXR file (%u tiles in total, peak memory usage: %u tiles)..",
m_blocksH * m_blocksV, m_peakUsage);
diff --git a/src/libbidir/SConscript b/src/libbidir/SConscript
index 5a78630c..69161888 100644
--- a/src/libbidir/SConscript
+++ b/src/libbidir/SConscript
@@ -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'
])
diff --git a/src/libcore/CMakeLists.txt b/src/libcore/CMakeLists.txt
index 91b0c081..5a36927a 100644
--- a/src/libcore/CMakeLists.txt
+++ b/src/libcore/CMakeLists.txt
@@ -70,6 +70,7 @@ set(HDRS
${INCLUDE_DIR}/thread.h
${INCLUDE_DIR}/timer.h
${INCLUDE_DIR}/tls.h
+ ${INCLUDE_DIR}/track.h
${INCLUDE_DIR}/transform.h
${INCLUDE_DIR}/triangle.h
${INCLUDE_DIR}/util.h
@@ -118,6 +119,7 @@ set(SRCS
thread.cpp
timer.cpp
tls.cpp
+ track.cpp
transform.cpp
triangle.cpp
util.cpp
diff --git a/src/libcore/SConscript b/src/libcore/SConscript
index 4673b927..bc403cd8 100644
--- a/src/libcore/SConscript
+++ b/src/libcore/SConscript
@@ -34,7 +34,7 @@ libcore_objects = [
'mstream.cpp', 'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp',
'zstream.cpp', 'shvector.cpp', 'fresolver.cpp', 'rfilter.cpp',
'quad.cpp', 'mmap.cpp', 'chisquare.cpp', 'warp.cpp', 'vmf.cpp',
- 'tls.cpp', 'ssemath.cpp', 'spline.cpp'
+ 'tls.cpp', 'ssemath.cpp', 'spline.cpp', 'track.cpp'
]
# Add some platform-specific components
diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp
index 4c450d4d..770c054d 100644
--- a/src/libcore/bitmap.cpp
+++ b/src/libcore/bitmap.cpp
@@ -1355,7 +1355,7 @@ void Bitmap::writePNG(Stream *stream, int compression) const {
png_text *text = NULL;
Properties metadata(m_metadata);
- metadata.setString("generated-by", "Mitsuba version " MTS_VERSION);
+ metadata.setString("generatedBy", "Mitsuba version " MTS_VERSION);
std::vector keys = metadata.getPropertyNames();
std::vector values(keys.size());
@@ -1923,7 +1923,7 @@ void Bitmap::writeOpenEXR(Stream *stream,
#endif
Properties metadata(m_metadata);
- metadata.setString("generated-by", "Mitsuba version " MTS_VERSION);
+ metadata.setString("generatedBy", "Mitsuba version " MTS_VERSION);
std::vector keys = metadata.getPropertyNames();
diff --git a/src/libcore/fmtconv.cpp b/src/libcore/fmtconv.cpp
index ae831528..7f8d3b73 100644
--- a/src/libcore/fmtconv.cpp
+++ b/src/libcore/fmtconv.cpp
@@ -156,7 +156,6 @@ template struct FormatConverterImpl : public FormatConverter {
precomp[i] = convertScalar(detail::safe_cast(i), sourceGamma, NULL, multiplier, invDestGamma);
}
- const DestFormat zero = convertScalar(0.0f);
const DestFormat one = convertScalar(1.0f);
Spectrum spec;
@@ -193,14 +192,14 @@ template struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZ:
for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma);
- *dest++ = zero; *dest++ = value; *dest++ = zero;
+ *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
}
break;
case Bitmap::EXYZA:
for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma);
- *dest++ = zero; *dest++ = value; *dest++ = zero; *dest++ = one;
+ *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value; *dest++ = one;
}
break;
@@ -271,7 +270,7 @@ template struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZ:
for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma);
- *dest++ = zero; *dest++ = value; *dest++ = zero;
+ *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
source++;
}
break;
@@ -279,7 +278,7 @@ template struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZA:
for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma);
- *dest++ = zero; *dest++ = value; *dest++ = zero;
+ *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
*dest++ = convertScalar(*source++);
}
break;
diff --git a/src/libcore/properties.cpp b/src/libcore/properties.cpp
index b5922a88..23ab813e 100644
--- a/src/libcore/properties.cpp
+++ b/src/libcore/properties.cpp
@@ -18,6 +18,7 @@
#include
#include
+#include
/* 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(&((*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 Properties::getAnimatedTransform(const std::string &name) const {
+ std::map::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(&it->second.data);
+ const Transform *result2 = boost::get(&it->second.data);
+
+ if (!result1 && !result2)
+ SLog(EError, "The property \"%s\" has the wrong type (expected or ). 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 Properties::getAnimatedTransform(const std::string &name, const AnimatedTransform *defVal) const {
+ std::map::const_iterator it = m_elements->find(name);
+ if (it == m_elements->end())
+ return defVal;
+ AnimatedTransform * const * result1 = boost::get(&it->second.data);
+ const Transform *result2 = boost::get(&it->second.data);
+
+ if (!result1 && !result2)
+ SLog(EError, "The property \"%s\" has the wrong type (expected or ). 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 Properties::getAnimatedTransform(const std::string &name, const Transform &defVal) const {
+ std::map::const_iterator it = m_elements->find(name);
+ if (it == m_elements->end())
+ return new AnimatedTransform(defVal);
+
+ AnimatedTransform * const * result1 = boost::get(&it->second.data);
+ const Transform *result2 = boost::get(&it->second.data);
+
+ if (!result1 && !result2)
+ SLog(EError, "The property \"%s\" has the wrong type (expected or ). 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 {
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 {
public:
EqualityVisitor(const ElementData *ref) : ref(ref) { }
- bool operator()(const bool &v) const { const bool *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const int64_t &v) const { const int64_t *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const Float &v) const { const Float *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const Point &v) const { const Point *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const Vector &v) const { const Vector *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const Transform &v) const { const Transform *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const Spectrum &v) const { const Spectrum *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const std::string &v) const { const std::string *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
- bool operator()(const Properties::Data &v) const { const Properties::Data *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const bool &v) const { const bool *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const int64_t &v) const { const int64_t *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const Float &v) const { const Float *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const Point &v) const { const Point *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const Vector &v) const { const Vector *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const Transform &v) const { const Transform *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const AnimatedTransform *v) const { AnimatedTransform * const *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const Spectrum &v) const { const Spectrum *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const std::string &v) const { const std::string *v2 = boost::get(ref); return v2 ? (v == *v2) : false; }
+ bool operator()(const Properties::Data &v) const { const Properties::Data *v2 = boost::get(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(*props.m_elements);
+
+ for (std::map::iterator it = m_elements->begin();
+ it != m_elements->end(); ++it) {
+ AnimatedTransform **trafo = boost::get(&(*it).second.data);
+ if (trafo)
+ (*trafo)->incRef();
+ }
}
Properties::~Properties() {
+ for (std::map::iterator it = m_elements->begin();
+ it != m_elements->end(); ++it) {
+ AnimatedTransform **trafo = boost::get(&(*it).second.data);
+ if (trafo)
+ (*trafo)->decRef();
+ }
+
delete m_elements;
}
void Properties::operator=(const Properties &props) {
+ for (std::map::iterator it = m_elements->begin();
+ it != m_elements->end(); ++it) {
+ AnimatedTransform **trafo = boost::get(&(*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::iterator it = m_elements->begin();
+ it != m_elements->end(); ++it) {
+ AnimatedTransform **trafo = boost::get(&(*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::iterator it = m_elements->find(name);
if (it == m_elements->end())
return false;
+ AnimatedTransform **trafo = boost::get(&(*it).second.data);
+ if (trafo)
+ (*trafo)->decRef();
m_elements->erase(it);
return true;
}
@@ -246,6 +350,14 @@ void Properties::putPropertyNames(std::vector &results) const {
results.push_back((*it).first);
}
+void Properties::copyAttribute(const Properties &properties,
+ const std::string &sourceName, const std::string &targetName) {
+ std::map::const_iterator it = properties.m_elements->find(sourceName);
+ if (it == properties.m_elements->end())
+ SLog(EError, "copyAttribute(): Could not find parameter \"%s\"!", sourceName.c_str());
+ m_elements->operator[](targetName) = it->second;
+}
+
bool Properties::operator==(const Properties &p) const {
if (m_pluginName != p.m_pluginName || m_id != p.m_id || m_elements->size() != p.m_elements->size())
return false;
diff --git a/src/librender/track.cpp b/src/libcore/track.cpp
similarity index 58%
rename from src/librender/track.cpp
rename to src/libcore/track.cpp
index e438d0f4..1e8a64a6 100644
--- a/src/librender/track.cpp
+++ b/src/libcore/track.cpp
@@ -1,8 +1,18 @@
-#include
+#include
#include
MTS_NAMESPACE_BEGIN
+AnimatedTransform::AnimatedTransform(const AnimatedTransform *trafo)
+ : m_transform(trafo->m_transform) {
+ m_tracks.reserve(trafo->getTrackCount());
+ for (size_t i=0; igetTrackCount(); ++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::infinity();
+ Float min = std::numeric_limits::infinity();
Float max = -std::numeric_limits::infinity();
for (size_t i=0; igetTime(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; igetType()) {
+ 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(track)->sortAndSimplify();
+ break;
+ case AbstractAnimationTrack::ETranslationXYZ:
+ case AbstractAnimationTrack::EScaleXYZ:
+ isNeeded = static_cast(track)->sortAndSimplify();
+ break;
+ case AbstractAnimationTrack::ERotationQuat:
+ isNeeded = static_cast(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; idecRef();
+ m_tracks.clear();
+ }
+}
+
+
+const AbstractAnimationTrack *AnimatedTransform::findTrack(AbstractAnimationTrack::EType type) const {
+ for (size_t i=0; igetType() == type)
+ return track;
+ }
+ return NULL;
+}
+AbstractAnimationTrack *AnimatedTransform::findTrack(AbstractAnimationTrack::EType type) {
+ for (size_t i=0; igetType() == 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 &result) const {
+ for (size_t i=0; igetSize(); ++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 {
diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp
index 6ecbff78..17214c62 100644
--- a/src/libcore/util.cpp
+++ b/src/libcore/util.cpp
@@ -28,13 +28,15 @@
#if defined(__OSX__)
#include
-#elif defined(WIN32)
+#include
+#elif defined(__WINDOWS__)
#include
+#include
#else
#include
#endif
-#if defined(WIN32)
+#if defined(__WINDOWS__)
# include
# include
# include
@@ -134,7 +136,7 @@ std::string memString(size_t size) {
}
void * __restrict allocAligned(size_t size) {
-#if defined(WIN32)
+#if defined(__WINDOWS__)
return _aligned_malloc(size, L1_CACHE_LINE_SIZE);
#elif defined(__OSX__)
/* OSX malloc already returns 16-byte aligned data suitable
@@ -146,7 +148,7 @@ void * __restrict allocAligned(size_t size) {
}
void freeAligned(void *ptr) {
-#if defined(WIN32)
+#if defined(__WINDOWS__)
_aligned_free(ptr);
#else
free(ptr);
@@ -154,7 +156,7 @@ void freeAligned(void *ptr) {
}
int getCoreCount() {
-#if defined(WIN32)
+#if defined(__WINDOWS__)
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);
return sys_info.dwNumberOfProcessors;
@@ -169,7 +171,45 @@ int getCoreCount() {
#endif
}
-#if defined(WIN32)
+size_t getPrivateMemoryUsage() {
+#if defined(__WINDOWS__)
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
+ return (size_t) pmc.PrivateUsage; /* Process-private memory usage (RAM + swap) */
+#elif defined(__OSX__)
+ struct task_basic_info_64 t_info;
+ mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_64_COUNT;
+
+ if (task_info(mach_task_self(), TASK_BASIC_INFO_64,
+ (task_info_t)&t_info, &t_info_count) != KERN_SUCCESS)
+ return 0;
+
+ return (size_t) t_info.resident_size; /* Not exactly what we want -- oh well.. */
+#else
+ FILE* file = fopen("/proc/self/status", "r");
+ if (!file)
+ return 0;
+
+ char buffer[128];
+ size_t result = 0;
+ while (fgets(buffer, sizeof(buffer), file) != NULL) {
+ if (strncmp(buffer, "VmRSS:", 6) != 0 && /* Non-swapped physical memory specific to this process */
+ strncmp(buffer, "VmSwap:", 7) != 0) /* Swapped memory specific to this process */
+ continue;
+
+ char *line = buffer;
+ while (*line < '0' || *line > '9')
+ ++line;
+ line[strlen(line)-3] = '\0';
+ result += (size_t) atoi(line) * 1024;
+ }
+
+ fclose(file);
+ return result;
+#endif
+}
+
+#if defined(__WINDOWS__)
std::string lastErrorText() {
DWORD errCode = GetLastError();
char *errorText = NULL;
@@ -192,7 +232,7 @@ std::string lastErrorText() {
bool enableFPExceptions() {
bool exceptionsWereEnabled = false;
-#if defined(WIN32)
+#if defined(__WINDOWS__)
_clearfp();
uint32_t cw = _controlfp(0, 0);
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
@@ -211,7 +251,7 @@ bool enableFPExceptions() {
bool disableFPExceptions() {
bool exceptionsWereEnabled = false;
-#if defined(WIN32)
+#if defined(__WINDOWS__)
_clearfp();
uint32_t cw = _controlfp(0, 0);
exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
@@ -230,7 +270,7 @@ bool disableFPExceptions() {
void restoreFPExceptions(bool oldState) {
bool currentState;
-#if defined(WIN32)
+#if defined(__WINDOWS__)
uint32_t cw = _controlfp(0, 0);
currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW);
#elif defined(__OSX__)
@@ -249,7 +289,7 @@ void restoreFPExceptions(bool oldState) {
std::string getHostName() {
char hostName[128];
if (gethostname(hostName, sizeof(hostName)) != 0)
-#if defined(WIN32)
+#if defined(__WINDOWS__)
SLog(EError, "Could not retrieve the computer's host name: %s!",
lastErrorText().c_str());
#else
@@ -280,7 +320,7 @@ std::string getFQDN() {
fqdn, NI_MAXHOST, NULL, 0, 0);
if (retVal != 0) {
freeaddrinfo(addrInfo);
-#if defined(WIN32)
+#if defined(__WINDOWS__)
SLog(EWarn, "Could not retrieve the computer's fully "
"qualified domain name: error %i!", WSAGetLastError());
#else
@@ -304,7 +344,7 @@ std::string formatString(const char *fmt, ...) {
char tmp[512];
va_list iterator;
-#if defined(WIN32)
+#if defined(__WINDOWS__)
va_start(iterator, fmt);
size_t size = _vscprintf(fmt, iterator) + 1;
diff --git a/src/libhw/SConscript b/src/libhw/SConscript
index c0ca0c82..34ef87af 100644
--- a/src/libhw/SConscript
+++ b/src/libhw/SConscript
@@ -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()
diff --git a/src/libhw/vpl.cpp b/src/libhw/vpl.cpp
index 79cd24e4..877d7715 100644
--- a/src/libhw/vpl.cpp
+++ b/src/libhw/vpl.cpp
@@ -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(shape);
const std::vector &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; jsetShader(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 geometryPermutation(m_geometry.size()),
+ opaqueGeometryPermutation(m_opaqueGeometry.size());
+
+ for (size_t i=0; i geometryPermutationInv(m_geometry.size()),
+ opaqueGeometryPermutationInv(m_opaqueGeometry.size());
+
+ for (size_t i=0; i= 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::infinity();
m_farClip = -std::numeric_limits::infinity();
+ /* Update animations */
+ for (size_t i=0; ieval(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
#include
-#include
+#include
#include