some work towards animated transformation support
parent
3885a4c6f9
commit
544080e808
|
@ -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']
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<xsd:element name="vector" type="point"/>
|
||||
<xsd:element name="boolean" type="boolean"/>
|
||||
<xsd:element name="transform" type="transform"/>
|
||||
<xsd:element name="animation" type="animation"/>
|
||||
<xsd:element name="string" type="string"/>
|
||||
<xsd:element name="spectrum" type="spectrum"/>
|
||||
<xsd:element name="rgb" type="rgb"/>
|
||||
|
@ -314,6 +315,23 @@
|
|||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="animationTransform">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="translate" type="translate"/>
|
||||
<xsd:element name="rotate" type="rotate"/>
|
||||
<xsd:element name="lookAt" type="lookAt"/>
|
||||
<xsd:element name="lookat" type="lookAt"/>
|
||||
<xsd:element name="scale" type="scale"/>
|
||||
<xsd:element name="matrix" type="matrix"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="time" type="doubleType" use="required"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="animation">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="transform" type="animationTransform"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="translate">
|
||||
<xsd:attribute name="x" type="doubleType"/>
|
||||
<xsd:attribute name="y" type="doubleType"/>
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
This file is part of Mitsuba, a physically based rendering system.
|
||||
|
||||
Copyright (c) 2007-2012 by Wenzel Jakob and others.
|
||||
|
||||
Mitsuba is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License Version 3
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
Mitsuba is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(__MITSUBA_CORE_MATH_H_)
|
||||
#define __MITSUBA_CORE_MATH_H_
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
namespace math {
|
||||
#if defined(__LINUX__) && defined(__x86_64__)
|
||||
/*
|
||||
The Linux/x86_64 single precision implementations of 'exp'
|
||||
and 'log' suffer from a serious performance regression.
|
||||
It is about 5x faster to use the double-precision versions
|
||||
with the extra overhead of the involved FP conversion.
|
||||
|
||||
Until this is fixed, the following aliases make sure that
|
||||
the fastest implementation is used in every case.
|
||||
*/
|
||||
inline float fastexp(float value) {
|
||||
return (float) ::exp((double) value);
|
||||
}
|
||||
|
||||
inline double fastexp(double value) {
|
||||
return ::exp(value);
|
||||
}
|
||||
|
||||
inline float fastlog(float value) {
|
||||
return (float) ::log((double) value);
|
||||
}
|
||||
|
||||
inline double fastlog(double value) {
|
||||
return ::log(value);
|
||||
}
|
||||
#else
|
||||
inline float fastexp(float value) {
|
||||
return ::expf(value);
|
||||
}
|
||||
|
||||
inline double fastexp(double value) {
|
||||
return ::exp(value);
|
||||
}
|
||||
|
||||
inline float fastlog(float value) {
|
||||
return ::logf(value);
|
||||
}
|
||||
|
||||
inline double fastlog(double value) {
|
||||
return ::log(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_GNU_SOURCE)
|
||||
inline void sincos(float theta, float *sin, float *cos) {
|
||||
::sincosf(theta, sin, cos);
|
||||
}
|
||||
|
||||
inline void sincos(double theta, double *sin, double *cos) {
|
||||
::sincos(theta, sin, cos);
|
||||
}
|
||||
|
||||
#else
|
||||
inline void sincos(float theta, float *_sin, float *_cos) {
|
||||
*_sin = sinf(theta);
|
||||
*_cos = cosf(theta);
|
||||
}
|
||||
|
||||
inline void sincos(double theta, double *_sin, double *_cos) {
|
||||
*_sin = sin(theta);
|
||||
*_cos = cos(theta);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Arcsine variant that gracefully handles arguments > 1 that are due to roundoff errors
|
||||
inline float safe_asin(float value) {
|
||||
return std::asin(std::min(1.0f, std::max(-1.0f, value)));
|
||||
}
|
||||
|
||||
/// Arcsine variant that gracefully handles arguments > 1 that are due to roundoff errors
|
||||
inline double safe_asin(double value) {
|
||||
return std::asin(std::min(1.0, std::max(-1.0, value)));
|
||||
}
|
||||
|
||||
/// Arccosine variant that gracefully handles arguments > 1 that are due to roundoff errors
|
||||
inline float safe_acos(float value) {
|
||||
return std::acos(std::min(1.0f, std::max(-1.0f, value)));
|
||||
}
|
||||
|
||||
/// Arccosine variant that gracefully handles arguments > 1 that are due to roundoff errors
|
||||
inline double safe_acos(double value) {
|
||||
return std::acos(std::min(1.0, std::max(-1.0, value)));
|
||||
}
|
||||
|
||||
/// Square root variant that gracefully handles arguments < 0 that are due to roundoff errors
|
||||
inline float safe_sqrt(float value) {
|
||||
return std::sqrt(std::max(0.0f, value));
|
||||
}
|
||||
|
||||
/// Square root variant that gracefully handles arguments < 0 that are due to roundoff errors
|
||||
inline double safe_sqrt(double value) {
|
||||
return std::sqrt(std::max(0.0, value));
|
||||
}
|
||||
|
||||
/// Simple signum function -- note that it returns the FP sign of the input (and never zero)
|
||||
inline Float signum(Float value) {
|
||||
#if defined(__WINDOWS__)
|
||||
return (Float) _copysign(1.0, value);
|
||||
#elif defined(SINGLE_PRECISION)
|
||||
return copysignf((float) 1.0, value);
|
||||
#elif defined(DOUBLE_PRECISION)
|
||||
return copysign((double) 1.0, value);
|
||||
#endif
|
||||
}
|
||||
}; /* namespace math */
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
extern "C" {
|
||||
extern MTS_EXPORT_CORE float nextafterf(float x, float y);
|
||||
extern MTS_EXPORT_CORE double nextafter(double x, double y);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* __MITSUBA_CORE_MATH_H_ */
|
|
@ -132,12 +132,17 @@ template <typename T> struct TQuaternion {
|
|||
|
||||
/// Equality test
|
||||
bool operator==(const TQuaternion &q) const {
|
||||
return v == q.v && v.w == q.w;
|
||||
return v == q.v && w == q.w;
|
||||
}
|
||||
|
||||
/// Inequality test
|
||||
bool operator!=(const TQuaternion &q) const {
|
||||
return v != q.v || v.w != q.w;
|
||||
return v != q.v || w != q.w;
|
||||
}
|
||||
|
||||
/// Identity test
|
||||
bool isIdentity() const {
|
||||
return v.isZero() && w == 1;
|
||||
}
|
||||
|
||||
/// Return the rotation axis of this quaternion
|
||||
|
@ -216,39 +221,42 @@ template <typename T> struct TQuaternion {
|
|||
}
|
||||
}
|
||||
|
||||
inline static TQuaternion fromTransform(const Transform &trafo) {
|
||||
return fromMatrix(trafo.getMatrix());
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Construct an unit quaternion matching the supplied
|
||||
* rotation matrix.
|
||||
*/
|
||||
static TQuaternion fromTransform(const Transform trafo) {
|
||||
static TQuaternion fromMatrix(const Matrix4x4 &m) {
|
||||
/// Implementation from PBRT
|
||||
const Matrix4x4 &m = trafo.getMatrix();
|
||||
T trace = m.m[0][0] + m.m[1][1] + m.m[2][2];
|
||||
T trace = m(0, 0) + m(1, 1) + m(2, 2);
|
||||
TVector3<T> v; T w;
|
||||
if (trace > 0.f) {
|
||||
// Compute w from matrix trace, then xyz
|
||||
// 4w^2 = m[0][0] + m[1][1] + m[2][2] + m[3][3] (but m[3][3] == 1)
|
||||
// 4w^2 = m[0, 0] + m[1, 1] + m[2, 2] + m[3, 3] (but m[3, 3] == 1)
|
||||
T s = std::sqrt(trace + 1.0f);
|
||||
w = s / 2.0f;
|
||||
s = 0.5f / s;
|
||||
v.x = (m.m[2][1] - m.m[1][2]) * s;
|
||||
v.y = (m.m[0][2] - m.m[2][0]) * s;
|
||||
v.z = (m.m[1][0] - m.m[0][1]) * s;
|
||||
v.x = (m(2, 1) - m(1, 2)) * s;
|
||||
v.y = (m(0, 2) - m(2, 0)) * s;
|
||||
v.z = (m(1, 0) - m(0, 1)) * s;
|
||||
} else {
|
||||
// Compute largest of $x$, $y$, or $z$, then remaining components
|
||||
const int nxt[3] = {1, 2, 0};
|
||||
T q[3];
|
||||
int i = 0;
|
||||
if (m.m[1][1] > m.m[0][0]) i = 1;
|
||||
if (m.m[2][2] > m.m[i][i]) i = 2;
|
||||
if (m(1, 1) > m(0, 0)) i = 1;
|
||||
if (m(2, 2) > m(i, i)) i = 2;
|
||||
int j = nxt[i];
|
||||
int k = nxt[j];
|
||||
T s = std::sqrt((m.m[i][i] - (m.m[j][j] + m.m[k][k])) + 1.0);
|
||||
T s = std::sqrt((m(i, i) - (m(j, j) + m(k, k))) + 1.0f);
|
||||
q[i] = s * 0.5f;
|
||||
if (s != 0.f) s = 0.5f / s;
|
||||
w = (m.m[k][j] - m.m[j][k]) * s;
|
||||
q[j] = (m.m[j][i] + m.m[i][j]) * s;
|
||||
q[k] = (m.m[k][i] + m.m[i][k]) * s;
|
||||
w = (m(k, j) - m(j, k)) * s;
|
||||
q[j] = (m(j, i) + m(i, j)) * s;
|
||||
q[k] = (m(k, i) + m(i, k)) * s;
|
||||
v.x = q[0];
|
||||
v.y = q[1];
|
||||
v.z = q[2];
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -46,6 +46,7 @@ using std::endl;
|
|||
#include <mitsuba/core/fwd.h>
|
||||
#include <mitsuba/render/fwd.h>
|
||||
#include <mitsuba/core/stl.h>
|
||||
#include <mitsuba/core/math.h>
|
||||
#include <mitsuba/core/object.h>
|
||||
#include <mitsuba/core/ref.h>
|
||||
#include <mitsuba/core/tls.h>
|
||||
|
|
|
@ -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<ETag, const Class *> TagEntry;
|
||||
|
@ -170,6 +171,7 @@ private:
|
|||
std::stack<ParseContext> m_context;
|
||||
TagMap m_tags;
|
||||
Transform m_transform;
|
||||
ref<AnimatedTransform> m_animatedTransform;
|
||||
bool m_isIncludedFile;
|
||||
};
|
||||
|
||||
|
|
|
@ -82,9 +82,9 @@ protected:
|
|||
*/
|
||||
template <typename T> class AnimationTrack : public AbstractAnimationTrack {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T ValueType;
|
||||
|
||||
AnimationTrack(EType type, size_t nKeyframes)
|
||||
AnimationTrack(EType type, size_t nKeyframes = 0)
|
||||
: AbstractAnimationTrack(type, nKeyframes), m_values(nKeyframes) { }
|
||||
|
||||
AnimationTrack(EType type, Stream *stream)
|
||||
|
@ -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<Float>::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<Float, ValueType> &p1,
|
||||
const std::pair<Float, ValueType> &p2) const {
|
||||
return p1.first < p2.first;
|
||||
}
|
||||
};
|
||||
|
||||
struct MatchPredicate {
|
||||
inline bool operator()(const std::pair<Float, ValueType> &p1,
|
||||
const std::pair<Float, ValueType> &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<Float, ValueType> > temp(m_values.size());
|
||||
for (size_t i=0; i<m_values.size(); ++i)
|
||||
temp[i] = std::make_pair(m_times[i], m_values[i]);
|
||||
std::sort(temp.begin(), temp.end(), SortPredicate());
|
||||
temp.erase(std::unique(temp.begin(), temp.end(), MatchPredicate()));
|
||||
m_times.resize(temp.size()); m_values.resize(temp.size());
|
||||
for (size_t i=0; i<temp.size(); ++i) {
|
||||
m_times[i] = temp[i].first;
|
||||
m_values[i] = temp[i].second;
|
||||
}
|
||||
|
||||
return m_values.size() > 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<value_type>();
|
||||
/// Is this a "no-op" transformation?
|
||||
inline bool isNoOp(const ValueType &value) const;
|
||||
|
||||
inline void unserialize(Stream *stream, ValueType &value) {
|
||||
value = stream->readElement<ValueType>();
|
||||
}
|
||||
|
||||
inline void serialize(Stream *stream, const value_type &value) const {
|
||||
stream->writeElement<value_type>(value);
|
||||
inline void serialize(Stream *stream, const ValueType &value) const {
|
||||
stream->writeElement<ValueType>(value);
|
||||
}
|
||||
private:
|
||||
std::vector<value_type> m_values;
|
||||
std::vector<ValueType> m_values;
|
||||
};
|
||||
|
||||
template<typename T> inline T AnimationTrack<T>::lerp(size_t idx0, size_t idx1, Float t) const {
|
||||
|
@ -150,6 +201,32 @@ template<> inline Quaternion AnimationTrack<Quaternion>::lerp(size_t idx0, size_
|
|||
return slerp(m_values[idx0], m_values[idx1], t);
|
||||
}
|
||||
|
||||
template<typename T> inline bool AnimationTrack<T>::isNoOp(const ValueType &value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<> inline bool AnimationTrack<Float>::isNoOp(const Float &value) const {
|
||||
if ((m_type == ETranslationX || m_type == ETranslationY || m_type == ETranslationZ) && value == 0)
|
||||
return true;
|
||||
else if ((m_type == ERotationX || m_type == ERotationY || m_type == ERotationZ) && value == 0)
|
||||
return true;
|
||||
else if ((m_type == EScaleX || m_type == EScaleY || m_type == EScaleZ) && value == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<> inline bool AnimationTrack<Vector>::isNoOp(const Vector &value) const {
|
||||
if (m_type == ETranslationXYZ && value.isZero())
|
||||
return true;
|
||||
else if (m_type == EScaleXYZ && (value.x == 1 && value.y == 1 && value.z == 1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<> inline bool AnimationTrack<Quaternion>::isNoOp(const Quaternion &value) const {
|
||||
return value.isIdentity();
|
||||
}
|
||||
|
||||
template<> inline void AnimationTrack<Point>::unserialize(Stream *stream, Point &value) {
|
||||
value = Point(stream);
|
||||
}
|
||||
|
@ -179,7 +256,7 @@ template<> inline void AnimationTrack<Quaternion>::serialize(Stream *stream, con
|
|||
* \ingroup librender
|
||||
*/
|
||||
class MTS_EXPORT_RENDER AnimatedTransform : public Object {
|
||||
protected:
|
||||
private:
|
||||
/// Internal functor used by \ref eval() and \ref SimpleCache
|
||||
struct MTS_EXPORT_RENDER TransformFunctor {
|
||||
public:
|
||||
|
@ -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);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <mitsuba/core/fresolver.h>
|
||||
#include <mitsuba/render/scene.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <Eigen/SVD>
|
||||
|
||||
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<VectorTrack> translation = new VectorTrack(VectorTrack::ETranslationXYZ);
|
||||
ref<QuatTrack> rotation = new QuatTrack(VectorTrack::ERotationQuat);
|
||||
ref<VectorTrack> scaling = new VectorTrack(VectorTrack::EScaleXYZ);
|
||||
translation->reserve(2);
|
||||
rotation->reserve(2);
|
||||
scaling->reserve(2);
|
||||
m_animatedTransform->addTrack(translation);
|
||||
m_animatedTransform->addTrack(rotation);
|
||||
m_animatedTransform->addTrack(scaling);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -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: {
|
||||
if (!m_animatedTransform.get()) {
|
||||
context.parent->properties.setTransform(
|
||||
context.attributes["name"], m_transform);
|
||||
} else {
|
||||
/* Compute the polar decomposition and insert into the animated transform
|
||||
uh oh.. we have to get rid of 2 matrix libraries at some point :) */
|
||||
typedef Eigen::Matrix<Float, 3, 3> EMatrix;
|
||||
const Matrix4x4 m = m_transform.getMatrix();
|
||||
|
||||
EMatrix A;
|
||||
A << m(0, 0), m(0, 1), m(0, 2),
|
||||
m(1, 0), m(1, 1), m(1, 2),
|
||||
m(2, 0), m(2, 1), m(2, 2);
|
||||
|
||||
Eigen::JacobiSVD<EMatrix> svd(A, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
EMatrix U = svd.matrixU(), V = svd.matrixV(), S = svd.singularValues().asDiagonal();
|
||||
|
||||
if (svd.singularValues().prod() < 0) {
|
||||
S = -S; U = -U;
|
||||
}
|
||||
|
||||
EMatrix Q = U*V.transpose();
|
||||
EMatrix P = V*S*V.transpose();
|
||||
|
||||
VectorTrack *translation = (VectorTrack *) m_animatedTransform->getTrack(0);
|
||||
QuatTrack *rotation = (QuatTrack *) m_animatedTransform->getTrack(1);
|
||||
VectorTrack *scaling = (VectorTrack *) m_animatedTransform->getTrack(2);
|
||||
|
||||
Float time = parseFloat("time", context.attributes["time"]);
|
||||
rotation->append(time, Quaternion::fromMatrix(
|
||||
Matrix4x4(
|
||||
Q(0, 0), Q(0, 1), Q(0, 2), 0.0f,
|
||||
Q(1, 0), Q(1, 1), Q(1, 2), 0.0f,
|
||||
Q(2, 0), Q(2, 1), Q(2, 2), 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
)
|
||||
));
|
||||
|
||||
scaling->append(time, Vector(P(0, 0), P(1, 1), P(2, 2)));
|
||||
translation->append(time, Vector(m(0, 3), m(1, 3), m(2, 3)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -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<Float>::infinity();
|
||||
Float max = -std::numeric_limits<Float>::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; i<m_tracks.size(); ++i) {
|
||||
AbstractAnimationTrack *track = m_tracks[i];
|
||||
bool success = false;
|
||||
switch (track->getType()) {
|
||||
case AbstractAnimationTrack::ETranslationX:
|
||||
case AbstractAnimationTrack::ETranslationY:
|
||||
case AbstractAnimationTrack::ETranslationZ:
|
||||
case AbstractAnimationTrack::ERotationX:
|
||||
case AbstractAnimationTrack::ERotationY:
|
||||
case AbstractAnimationTrack::ERotationZ:
|
||||
case AbstractAnimationTrack::EScaleX:
|
||||
case AbstractAnimationTrack::EScaleY:
|
||||
case AbstractAnimationTrack::EScaleZ:
|
||||
success = static_cast<FloatTrack *>(track)->sortAndSimplify();
|
||||
break;
|
||||
case AbstractAnimationTrack::ETranslationXYZ:
|
||||
case AbstractAnimationTrack::EScaleXYZ:
|
||||
success = static_cast<VectorTrack *>(track)->sortAndSimplify();
|
||||
break;
|
||||
case AbstractAnimationTrack::ERotationQuat:
|
||||
success = static_cast<QuatTrack *>(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; i<m_tracks.size(); ++i)
|
||||
m_tracks[i]->decRef();
|
||||
m_tracks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedTransform::serialize(Stream *stream) const {
|
||||
stream->writeSize(m_tracks.size());
|
||||
if (m_tracks.size() == 0) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue