mitsuba/include/mitsuba/core/quat.h

370 lines
9.6 KiB
C++

/*
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_QUAT_H_)
#define __MITSUBA_CORE_QUAT_H_
#include <mitsuba/core/transform.h>
MTS_NAMESPACE_BEGIN
/**
* \brief Parameterizable quaternion data structure
* \ingroup libcore
*/
template <typename T> struct TQuaternion {
typedef T Scalar;
/// Used by \ref TQuaternion::fromEulerAngles
enum EEulerAngleConvention {
EEulerXYZ = 0,
EEulerXZY,
EEulerYXZ,
EEulerYZX,
EEulerZXY,
EEulerZYX
};
/// Imaginary component
TVector3<T> v;
/// Real component
T w;
/// Create a unit quaternion
TQuaternion() : v(0.0f), w(1) { }
/**
* Initialize the quaternion with the specified
* real and imaginary components
*/
TQuaternion(const TVector3<T> &v, T w) : v(v), w(w) { }
/// Unserialize a quaternion from a binary data stream
explicit TQuaternion(Stream *stream) {
v = TVector3<T>(stream);
w = stream->readElement<T>();
}
/// Add two quaternions and return the result
TQuaternion operator+(const TQuaternion &q) const {
return TQuaternion(v + q.v, w + q.w);
}
/// Subtract two quaternions and return the result
TQuaternion operator-(const TQuaternion &q) const {
return TQuaternion(v - q.v, w - q.w);
}
/// Add another quaternions to the current one
TQuaternion& operator+=(const TQuaternion &q) {
v += q.v; w += q.w;
return *this;
}
/// Subtract a quaternion
TQuaternion& operator-=(const TQuaternion &q) {
v -= q.v; w -= q.w;
return *this;
}
/// Multiply the quaternion by the given scalar and return the result
TQuaternion operator*(T f) const {
return TQuaternion(v*f, w*f);
}
/// Multiply the quaternion by the given scalar
TQuaternion &operator*=(T f) {
v *= f; w *= f;
return *this;
}
/// Divide the quaternion by the given scalar and return the result
TQuaternion operator/(T f) const {
#ifdef MTS_DEBUG
if (f == 0)
SLog(EWarn, "Quaternion: Division by zero!");
#endif
T recip = (T) 1 / f;
return TQuaternion(v * recip, w * recip);
}
/// Divide the quaternion by the given scalar
TQuaternion &operator/=(T f) {
#ifdef MTS_DEBUG
if (f == 0)
SLog(EWarn, "Quaternion: Division by zero!");
#endif
T recip = (T) 1 / f;
v *= recip; w *= recip;
return *this;
}
/// Quaternion multiplication
TQuaternion &operator*=(const TQuaternion &q) {
Scalar tmp = w * q.w - dot(v, q.v);
v = cross(v, q.v) + q.w * v + w * q.v;
w = tmp;
return *this;
}
/// Quaternion multiplication (creates a temporary)
TQuaternion operator*(const TQuaternion &q) const {
return TQuaternion(cross(v, q.v) + q.w * v + w * q.v,
w * q.w - dot(v, q.v));
}
/// Equality test
bool operator==(const TQuaternion &q) const {
return v == q.v && w == q.w;
}
/// Inequality test
bool operator!=(const TQuaternion &q) const {
return v != q.v || w != q.w;
}
/// Identity test
bool isIdentity() const {
return v.isZero() && w == 1;
}
/// Return the rotation axis of this quaternion
inline TVector3<T> axis() const { return normalize(v); }
/// Return the rotation angle of this quaternion (in radians)
inline Scalar angle() const { return 2 * math::safe_acos(w); }
/**
* \brief Compute the exponential of a quaternion with
* scalar part w = 0.
*
* Based on code the appendix of
* "Quaternion Calculus for Computer Graphics" by Ken Shoemake
*/
TQuaternion exp() const {
T theta = v.length();
T c = std::cos(theta);
if (theta > Epsilon)
return TQuaternion(v * (std::sin(theta) / theta), c);
else
return TQuaternion(v, c);
}
/**
* \brief Compute the natural logarithm of a unit quaternion
*
* Based on code the appendix of
* "Quaternion Calculus for Computer Graphics" by Ken Shoemake
*/
TQuaternion log() const {
T scale = v.length();
T theta = std::atan2(scale, w);
if (scale > 0)
scale = theta/scale;
return TQuaternion<T>(v * scale, 0.0f);
}
/**
* \brief Construct an unit quaternion, which represents a rotation
* around \a axis by \a angle radians.
*/
static TQuaternion fromAxisAngle(const Vector &axis, Float angle) {
T sinValue = std::sin(angle/2.0f), cosValue = std::cos(angle/2.0f);
return TQuaternion(normalize(axis) * sinValue, cosValue);
}
/**
* \brief Construct an unit quaternion, which rotates unit direction
* \a from onto \a to.
*/
static TQuaternion fromDirectionPair(const Vector &from, const Vector &to) {
Float dp = dot(from, to);
if (dp > 1-Epsilon) {
// there is nothing to do
return TQuaternion();
} else if (dp < -(1-Epsilon)) {
// Use a better-conditioned method for opposite directions
Vector rotAxis = cross(from, Vector(1, 0, 0));
Float length = rotAxis.length();
if (length < Epsilon) {
rotAxis = cross(from, Vector(0, 1, 0));
length = rotAxis.length();
}
rotAxis /= length;
return TQuaternion(rotAxis, 0);
} else {
// Find cos(theta) and sin(theta) using half-angle formulae
Float cosTheta = std::sqrt(0.5f * (1 + dp));
Float sinTheta = std::sqrt(0.5f * (1 - dp));
Vector rotAxis = normalize(cross(from, to));
return TQuaternion(rotAxis * sinTheta, cosTheta);
}
}
inline static TQuaternion fromTransform(const Transform &trafo) {
return fromMatrix(trafo.getMatrix());
}
/**
* \brief Construct an unit quaternion matching the supplied
* rotation matrix.
*/
static TQuaternion fromMatrix(const Matrix4x4 &m) {
/// Implementation from PBRT
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)
T s = std::sqrt(trace + 1.0f);
w = s / 2.0f;
s = 0.5f / 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(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(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(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];
}
return TQuaternion(v, w);
}
/**
* \brief Construct an unit quaternion matching the supplied
* rotation expressed in Euler angles (in radians)
*/
static TQuaternion fromEulerAngles(EEulerAngleConvention conv,
Float x, Float y, Float z) {
Quaternion qx = fromAxisAngle(Vector(1.0, 0.0, 0.0), x);
Quaternion qy = fromAxisAngle(Vector(0.0, 1.0, 0.0), y);
Quaternion qz = fromAxisAngle(Vector(0.0, 0.0, 1.0), z);
switch (conv) {
case EEulerXYZ:
return qz * qy * qx;
case EEulerXZY:
return qy * qz * qx;
case EEulerYXZ:
return qz * qx * qy;
case EEulerYZX:
return qx * qz * qy;
case EEulerZXY:
return qy * qx * qz;
case EEulerZYX:
return qx * qy * qz;
default:
SLog(EError, "Internal error!");
return TQuaternion();
}
}
/// Compute the rotation matrix for the given quaternion
Transform toTransform() const {
/// Implementation from PBRT
Float xx = v.x * v.x, yy = v.y * v.y, zz = v.z * v.z;
Float xy = v.x * v.y, xz = v.x * v.z, yz = v.y * v.z;
Float wx = v.x * w, wy = v.y * w, wz = v.z * w;
Matrix4x4 m;
m.m[0][0] = 1.f - 2.f * (yy + zz);
m.m[0][1] = 2.f * (xy + wz);
m.m[0][2] = 2.f * (xz - wy);
m.m[0][3] = 0.0f;
m.m[1][0] = 2.f * (xy - wz);
m.m[1][1] = 1.f - 2.f * (xx + zz);
m.m[1][2] = 2.f * (yz + wx);
m.m[1][3] = 0.0f;
m.m[2][0] = 2.f * (xz + wy);
m.m[2][1] = 2.f * (yz - wx);
m.m[2][2] = 1.f - 2.f * (xx + yy);
m.m[2][3] = 0.0f;
m.m[3][0] = 0.0f;
m.m[3][1] = 0.0f;
m.m[3][2] = 0.0f;
m.m[3][3] = 1.0f;
Matrix4x4 transp;
m.transpose(transp);
return Transform(transp, m);
}
/// Serialize this quaternion to a binary data stream
void serialize(Stream *stream) const {
v.serialize(stream);
stream->writeElement<T>(w);
}
/// Return a readable string representation of this quaternion
std::string toString() const {
std::ostringstream oss;
oss << "Quaternion[v=" << v.toString() << ", w=" << w << "]";
return oss.str();
}
};
template <typename T> inline TQuaternion<T> operator*(T f, const TQuaternion<T> &v) {
return v*f;
}
template <typename T> inline T dot(const TQuaternion<T> &q1, const TQuaternion<T> &q2) {
return dot(q1.v, q2.v) + q1.w * q2.w;
}
template <typename T> inline TQuaternion<T> normalize(const TQuaternion<T> &q) {
return q / std::sqrt(dot(q, q));
}
template <typename T> inline TQuaternion<T> slerp(const TQuaternion<T> &q1,
const TQuaternion<T> &q2, Float t) {
T cosTheta = dot(q1, q2);
if (cosTheta > .9995f) {
// Revert to plain linear interpolation
return normalize(q1 * (1.0f - t) + q2 * t);
} else {
Float theta = math::safe_acos(clamp(cosTheta, (Float) -1.0f, (Float) 1.0f));
Float thetap = theta * t;
TQuaternion<T> qperp = normalize(q2 - q1 * cosTheta);
return q1 * std::cos(thetap) + qperp * std::sin(thetap);
}
}
MTS_NAMESPACE_END
#endif /* __MITSUBA_CORE_QUAT_H_ */