From 544080e808ace8aa6ecbacf06ab58ddee65058a6 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 26 Oct 2012 02:15:32 -0400 Subject: [PATCH] some work towards animated transformation support --- build/config-macos10.7-gcc-x86_64.py | 2 +- data/schema/scene.xsd | 28 ++++- include/mitsuba/core/math.h | 142 ++++++++++++++++++++++++++ include/mitsuba/core/quat.h | 38 ++++--- include/mitsuba/core/stl.h | 117 +-------------------- include/mitsuba/mitsuba.h | 1 + include/mitsuba/render/scenehandler.h | 4 +- include/mitsuba/render/track.h | 109 +++++++++++++++++--- src/converter/SConscript | 2 +- src/libbidir/SConscript | 2 +- src/libcore/SConscript | 6 +- src/libhw/SConscript | 6 +- src/librender/SConscript | 10 +- src/librender/scenehandler.cpp | 69 ++++++++++++- src/librender/track.cpp | 64 +++++++++--- src/mtsgui/SConscript | 4 +- src/shapes/cylinder.cpp | 2 +- src/tests/SConscript | 2 +- 18 files changed, 420 insertions(+), 188 deletions(-) create mode 100644 include/mitsuba/core/math.h 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/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/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/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 05eb48ef..bf0276ff 100644 --- a/include/mitsuba/core/stl.h +++ b/include/mitsuba/core/stl.h @@ -103,121 +103,6 @@ namespace std { using std::select2nd; using std::compose1; #endif +/// \endcond -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 #endif /* __MITSUBA_CORE_STL_H_ */ 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/scenehandler.h b/include/mitsuba/render/scenehandler.h index c8a4a149..ed8cf018 100644 --- a/include/mitsuba/render/scenehandler.h +++ b/include/mitsuba/render/scenehandler.h @@ -155,7 +155,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; @@ -170,6 +171,7 @@ private: std::stack m_context; TagMap m_tags; Transform m_transform; + ref m_animatedTransform; bool m_isIncludedFile; }; diff --git a/include/mitsuba/render/track.h b/include/mitsuba/render/track.h index 25049a3d..8b3a9523 100644 --- a/include/mitsuba/render/track.h +++ b/include/mitsuba/render/track.h @@ -82,9 +82,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) @@ -96,10 +96,19 @@ public: } /// 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); + } /// Serialize to a binary data stream inline void serialize(Stream *stream) const { @@ -111,7 +120,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::const_iterator entry = std::lower_bound(m_times.begin(), m_times.end(), time); @@ -126,19 +135,61 @@ 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 MatchPredicate { + inline bool operator()(const std::pair &p1, + const std::pair &p2) const { + return p1.first == p2.first || p1.second == p2.second; + } + }; + +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 unnecessary + * after the cleanup (for instance, it only contains (0,0,0) translation operations) + */ + bool sortAndSimplify() { + SAssert(m_values.size() == m_times.size()); + std::vector< std::pair > temp(m_values.size()); + for (size_t i=0; i 0 || !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(); + /// Is this a "no-op" transformation? + inline bool isNoOp(const ValueType &value) 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 +201,32 @@ template<> inline Quaternion AnimationTrack::lerp(size_t idx0, size_ return slerp(m_values[idx0], m_values[idx1], t); } +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); } @@ -179,7 +256,7 @@ template<> inline void AnimationTrack::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: @@ -221,7 +298,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 +307,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); diff --git a/src/converter/SConscript b/src/converter/SConscript index 626a9906..2c6b8b2b 100644 --- a/src/converter/SConscript +++ b/src/converter/SConscript @@ -18,7 +18,7 @@ if hasCollada: colladaEnv.StaticObject('obj.cpp'), colladaEnv.StaticObject('converter.cpp') ] - colladaEnv.Program('mtsimport', stubs + ['mtsimport.cpp'] + colladaEnv.Program('mtsimport', stubs + ['mtsimport.cpp'] + converter_objects) Return('converter_objects') 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/SConscript b/src/libcore/SConscript index 62ba5036..eeb437a0 100644 --- a/src/libcore/SConscript +++ b/src/libcore/SConscript @@ -28,10 +28,10 @@ libcore_objects = [ 'class.cpp', 'object.cpp', 'statistics.cpp', 'thread.cpp', 'brent.cpp', 'logger.cpp', 'appender.cpp', 'formatter.cpp', 'lock.cpp', 'qmc.cpp', 'random.cpp', 'timer.cpp', 'util.cpp', 'properties.cpp', 'half.cpp', - 'transform.cpp', 'spectrum.cpp', 'aabb.cpp', 'stream.cpp', + 'transform.cpp', 'spectrum.cpp', 'aabb.cpp', 'stream.cpp', 'fstream.cpp', 'plugin.cpp', 'triangle.cpp', 'bitmap.cpp', - 'fmtconv.cpp', 'serialization.cpp', 'sstream.cpp', 'cstream.cpp', - 'mstream.cpp', 'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp', + 'fmtconv.cpp', 'serialization.cpp', 'sstream.cpp', 'cstream.cpp', + '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' 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/librender/SConscript b/src/librender/SConscript index 3404e1b5..fc049f2f 100644 --- a/src/librender/SConscript +++ b/src/librender/SConscript @@ -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' ]) diff --git a/src/librender/scenehandler.cpp b/src/librender/scenehandler.cpp index 30017839..d44d1c79 100644 --- a/src/librender/scenehandler.cpp +++ b/src/librender/scenehandler.cpp @@ -31,6 +31,7 @@ #include #include #include +#include MTS_NAMESPACE_BEGIN XERCES_CPP_NAMESPACE_USE @@ -97,6 +98,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["animation"] = TagEntry(EAnimation, (Class *) NULL); m_tags["include"] = TagEntry(EInclude, (Class *) NULL); m_tags["alias"] = TagEntry(EAlias, (Class *) NULL); @@ -167,11 +169,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()); @@ -240,6 +243,19 @@ void SceneHandler::startElement(const XMLCh* const xmlName, case ETransform: m_transform = Transform(); break; + case EAnimation: { + m_animatedTransform = new AnimatedTransform(); + ref translation = new VectorTrack(VectorTrack::ETranslationXYZ); + ref rotation = new QuatTrack(VectorTrack::ERotationQuat); + ref 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; } @@ -575,9 +591,56 @@ void SceneHandler::endElement(const XMLCh* const xmlName) { } break; + case EAnimation: { + m_animatedTransform->sortAndSimplify(); +// context.parent->properties.setAnimatedTransform( +// context.attributes["name"], m_animatedTransform);//XXX + 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 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 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; diff --git a/src/librender/track.cpp b/src/librender/track.cpp index e438d0f4..ea94b217 100644 --- a/src/librender/track.cpp +++ b/src/librender/track.cpp @@ -48,16 +48,7 @@ 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 max = -std::numeric_limits::infinity(); @@ -70,15 +61,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 +135,53 @@ 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: + success = static_cast(track)->sortAndSimplify(); + break; + case AbstractAnimationTrack::ETranslationXYZ: + case AbstractAnimationTrack::EScaleXYZ: + success = static_cast(track)->sortAndSimplify(); + break; + case AbstractAnimationTrack::ERotationQuat: + success = static_cast(track)->sortAndSimplify(); + break; + default: + Log(EError, "Encountered an unsupported " + "animation track type: %i!", track->getType()); + } + if (success) { + isStatic &= track->getSize() == 1; + } else { + m_tracks.erase(m_tracks.begin() + i); + track->decRef(); + } + } + + if (isStatic) { + Transform temp; + temp = eval(0); + m_transform = temp; + for (size_t i=0; idecRef(); + m_tracks.clear(); + } +} + void AnimatedTransform::serialize(Stream *stream) const { stream->writeSize(m_tracks.size()); if (m_tracks.size() == 0) { diff --git a/src/mtsgui/SConscript b/src/mtsgui/SConscript index 19f0f37e..454e5f9b 100644 --- a/src/mtsgui/SConscript +++ b/src/mtsgui/SConscript @@ -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) @@ -59,7 +59,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() diff --git a/src/shapes/cylinder.cpp b/src/shapes/cylinder.cpp index 13d91e6b..5aea8ec9 100644 --- a/src/shapes/cylinder.cpp +++ b/src/shapes/cylinder.cpp @@ -495,7 +495,7 @@ public: void getNormalDerivative(const Intersection &its, Vector &dndu, Vector &dndv, bool shadingFrame) const { - dndu = its.dpdu / (m_radius * m_flipNormals ? -1 : 1); + dndu = its.dpdu / (m_radius * (m_flipNormals ? -1 : 1)); dndv = Vector(0.0f); } diff --git a/src/tests/SConscript b/src/tests/SConscript index fc42deae..0dc60491 100644 --- a/src/tests/SConscript +++ b/src/tests/SConscript @@ -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)