319 lines
11 KiB
C++
319 lines
11 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2011 by Wenzel Jakob and others.
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License Version 3
|
|
as published by the Free Software Foundation.
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#if !defined(__TRANSFORM_H)
|
|
#define __TRANSFORM_H
|
|
|
|
#include <mitsuba/core/matrix.h>
|
|
#include <mitsuba/core/ray.h>
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
/**
|
|
* \brief Encapsulates a 4x4 linear transformation and its inverse
|
|
* \ingroup libcore
|
|
*/
|
|
struct MTS_EXPORT_CORE Transform {
|
|
public:
|
|
/// Create an identity transformation
|
|
Transform() {
|
|
m_transform.setIdentity();
|
|
m_invTransform.setIdentity();
|
|
}
|
|
|
|
/// Unserialize a transformation from a stream
|
|
inline Transform(Stream *stream) {
|
|
m_transform = Matrix4x4(stream);
|
|
m_invTransform = Matrix4x4(stream);
|
|
}
|
|
|
|
/** \brief Create a transform from the given matrix
|
|
* and calculate the inverse
|
|
*/
|
|
Transform(const Matrix4x4 &trafo)
|
|
: m_transform(trafo) {
|
|
bool success = m_transform.invert(m_invTransform);
|
|
if (!success)
|
|
SLog(EError, "Unable to invert singular matrix %s", trafo.toString().c_str());
|
|
}
|
|
|
|
/// Create a transform from the given matrices
|
|
Transform(const Matrix4x4 &trafo, const Matrix4x4 &invTrafo)
|
|
: m_transform(trafo), m_invTransform(invTrafo) {
|
|
}
|
|
|
|
/// Return the inverse transform
|
|
Transform inverse() const {
|
|
return Transform(m_invTransform, m_transform);
|
|
}
|
|
|
|
/// Matrix-matrix multiplication
|
|
Transform operator*(const Transform &t) const;
|
|
|
|
/// Return the determinant of the upper left 3x3 submatrix
|
|
inline Float det3x3() const {
|
|
return m_transform.det3x3();
|
|
}
|
|
|
|
/// Test for a scale component
|
|
inline bool hasScale() const {
|
|
for (int i=0; i<3; ++i) {
|
|
for (int j=i; j<3; ++j) {
|
|
Float sum = 0;
|
|
for (int k=0; k<3; ++k)
|
|
sum += m_transform.m[i][k] * m_transform.m[j][k];
|
|
|
|
if (i == j && std::abs(sum-1) > 1e-3f)
|
|
return true;
|
|
else if (i != j && std::abs(sum) > 1e-3f)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Test if this is the identity matrix
|
|
inline bool isIdentity() const {
|
|
for (int i=0; i<4; ++i) {
|
|
for (int j=0; j<4; ++j) {
|
|
if (m_transform.m[i][j] != ((i==j) ? 1 : 0))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Matrix-vector multiplication for points in 3d space
|
|
inline Point operator()(const Point &p) const {
|
|
Float x = m_transform.m[0][0] * p.x + m_transform.m[0][1] * p.y
|
|
+ m_transform.m[0][2] * p.z + m_transform.m[0][3];
|
|
Float y = m_transform.m[1][0] * p.x + m_transform.m[1][1] * p.y
|
|
+ m_transform.m[1][2] * p.z + m_transform.m[1][3];
|
|
Float z = m_transform.m[2][0] * p.x + m_transform.m[2][1] * p.y
|
|
+ m_transform.m[2][2] * p.z + m_transform.m[2][3];
|
|
Float w = m_transform.m[3][0] * p.x + m_transform.m[3][1] * p.y
|
|
+ m_transform.m[3][2] * p.z + m_transform.m[3][3];
|
|
#ifdef MTS_DEBUG
|
|
if (w == 0)
|
|
SLog(EWarn, "w==0 in Transform::operator(Point &)");
|
|
#endif
|
|
if (w == 1.0f)
|
|
return Point(x, y, z);
|
|
else
|
|
return Point(x, y, z) / w;
|
|
}
|
|
|
|
/// Transform a point by a affine / non-projective matrix
|
|
inline Point transformAffine(const Point &p) const {
|
|
Float x = m_transform.m[0][0] * p.x + m_transform.m[0][1] * p.y
|
|
+ m_transform.m[0][2] * p.z + m_transform.m[0][3];
|
|
Float y = m_transform.m[1][0] * p.x + m_transform.m[1][1] * p.y
|
|
+ m_transform.m[1][2] * p.z + m_transform.m[1][3];
|
|
Float z = m_transform.m[2][0] * p.x + m_transform.m[2][1] * p.y
|
|
+ m_transform.m[2][2] * p.z + m_transform.m[2][3];
|
|
return Point(x,y,z);
|
|
}
|
|
|
|
/// Matrix-vector multiplication for points in 3d space (no temporaries)
|
|
inline void operator()(const Point &p, Point &dest) const {
|
|
dest.x = m_transform.m[0][0] * p.x + m_transform.m[0][1] * p.y
|
|
+ m_transform.m[0][2] * p.z + m_transform.m[0][3];
|
|
dest.y = m_transform.m[1][0] * p.x + m_transform.m[1][1] * p.y
|
|
+ m_transform.m[1][2] * p.z + m_transform.m[1][3];
|
|
dest.z = m_transform.m[2][0] * p.x + m_transform.m[2][1] * p.y
|
|
+ m_transform.m[2][2] * p.z + m_transform.m[2][3];
|
|
Float w = m_transform.m[3][0] * p.x + m_transform.m[3][1] * p.y
|
|
+ m_transform.m[3][2] * p.z + m_transform.m[3][3];
|
|
|
|
#ifdef MTS_DEBUG
|
|
if (w == 0)
|
|
SLog(EWarn, "w==0 in Transform::operator(Point &, Point &)");
|
|
#endif
|
|
if (w != 1.0f)
|
|
dest /= w;
|
|
}
|
|
|
|
/// Matrix-vector multiplication for vectors in 3d space
|
|
inline Vector operator()(const Vector &v) const {
|
|
Float x = m_transform.m[0][0] * v.x + m_transform.m[0][1] * v.y
|
|
+ m_transform.m[0][2] * v.z;
|
|
Float y = m_transform.m[1][0] * v.x + m_transform.m[1][1] * v.y
|
|
+ m_transform.m[1][2] * v.z;
|
|
Float z = m_transform.m[2][0] * v.x + m_transform.m[2][1] * v.y
|
|
+ m_transform.m[2][2] * v.z;
|
|
return Vector(x, y, z);
|
|
}
|
|
|
|
/// Matrix-vector multiplication for vectors in 3d space (no temporaries)
|
|
inline void operator()(const Vector &v, Vector &dest) const {
|
|
dest.x = m_transform.m[0][0] * v.x + m_transform.m[0][1] * v.y
|
|
+ m_transform.m[0][2] * v.z;
|
|
dest.y = m_transform.m[1][0] * v.x + m_transform.m[1][1] * v.y
|
|
+ m_transform.m[1][2] * v.z;
|
|
dest.z = m_transform.m[2][0] * v.x + m_transform.m[2][1] * v.y
|
|
+ m_transform.m[2][2] * v.z;
|
|
}
|
|
|
|
/// Matrix-normal multiplication
|
|
inline Normal operator()(const Normal &v) const {
|
|
Float x = m_invTransform.m[0][0] * v.x + m_invTransform.m[1][0] * v.y
|
|
+ m_invTransform.m[2][0] * v.z;
|
|
Float y = m_invTransform.m[0][1] * v.x + m_invTransform.m[1][1] * v.y
|
|
+ m_invTransform.m[2][1] * v.z;
|
|
Float z = m_invTransform.m[0][2] * v.x + m_invTransform.m[1][2] * v.y
|
|
+ m_invTransform.m[2][2] * v.z;
|
|
return Normal(x, y, z);
|
|
}
|
|
|
|
/// Matrix-normal multiplication (no temporaries)
|
|
inline void operator()(const Normal &v, Normal &dest) const {
|
|
dest.x = m_invTransform.m[0][0] * v.x + m_invTransform.m[1][0] * v.y
|
|
+ m_invTransform.m[2][0] * v.z;
|
|
dest.y = m_invTransform.m[0][1] * v.x + m_invTransform.m[1][1] * v.y
|
|
+ m_invTransform.m[2][1] * v.z;
|
|
dest.z = m_invTransform.m[0][2] * v.x + m_invTransform.m[1][2] * v.y
|
|
+ m_invTransform.m[2][2] * v.z;
|
|
}
|
|
|
|
/// 4D matrix-vector multiplication
|
|
inline Vector4 operator()(const Vector4 &v) const {
|
|
Float x = m_transform.m[0][0] * v.x + m_transform.m[0][1] * v.y
|
|
+ m_transform.m[0][2] * v.z + m_transform.m[0][3] * v.w;
|
|
Float y = m_transform.m[1][0] * v.x + m_transform.m[1][1] * v.y
|
|
+ m_transform.m[1][2] * v.z + m_transform.m[1][3] * v.w;
|
|
Float z = m_transform.m[2][0] * v.x + m_transform.m[2][1] * v.y
|
|
+ m_transform.m[2][2] * v.z + m_transform.m[2][3] * v.w;
|
|
Float w = m_transform.m[3][0] * v.x + m_transform.m[3][1] * v.y
|
|
+ m_transform.m[3][2] * v.z + m_transform.m[3][3] * v.w;
|
|
return Vector4(x,y,z,w);
|
|
}
|
|
|
|
/// 4D matrix-vector multiplication
|
|
inline void operator()(const Vector4 &v, Vector4 &dest) const {
|
|
dest.x = m_transform.m[0][0] * v.x + m_transform.m[0][1] * v.y
|
|
+ m_transform.m[0][2] * v.z + m_transform.m[0][3] * v.w;
|
|
dest.y = m_transform.m[1][0] * v.x + m_transform.m[1][1] * v.y
|
|
+ m_transform.m[1][2] * v.z + m_transform.m[1][3] * v.w;
|
|
dest.z = m_transform.m[2][0] * v.x + m_transform.m[2][1] * v.y
|
|
+ m_transform.m[2][2] * v.z + m_transform.m[2][3] * v.w;
|
|
dest.w = m_transform.m[3][0] * v.x + m_transform.m[3][1] * v.y
|
|
+ m_transform.m[3][2] * v.z + m_transform.m[3][3] * v.w;
|
|
}
|
|
|
|
/// Transform a ray. Assumes that there is no scaling
|
|
inline void operator()(const Ray &a, Ray &b) const {
|
|
b.mint = a.mint;
|
|
b.maxt = a.maxt;
|
|
operator()(a.o, b.o);
|
|
operator()(a.d, b.d);
|
|
#ifdef MTS_DEBUG_FP
|
|
bool state = disableFPExceptions();
|
|
#endif
|
|
/* Re-compute the reciprocal */
|
|
b.dRcp.x = 1.0f / b.d.x;
|
|
b.dRcp.y = 1.0f / b.d.y;
|
|
b.dRcp.z = 1.0f / b.d.z;
|
|
b.time = a.time;
|
|
#ifdef MTS_DEBUG_FP
|
|
restoreFPExceptions(state);
|
|
#endif
|
|
}
|
|
|
|
/// Return the underlying matrix
|
|
inline const Matrix4x4 &getMatrix() const { return m_transform; }
|
|
|
|
/// Return the underlying inverse matrix (const version)
|
|
inline const Matrix4x4 &getInverseMatrix() const { return m_invTransform; }
|
|
|
|
/// Create a translation transformation
|
|
static Transform translate(const Vector &v);
|
|
|
|
/// Create a rotation transformation around an arbitrary axis. The angle is specified in degrees
|
|
static Transform rotate(const Vector &axis, Float angle);
|
|
|
|
/// Create a scale transformation
|
|
static Transform scale(const Vector &v);
|
|
|
|
/** \brief Create a perspective transformation.
|
|
* (Maps [near, far] to [0, 1])
|
|
* \param fov Field of view in degrees
|
|
* \param clipNear Near clipping plane
|
|
* \param clipFar Far clipping plane
|
|
*/
|
|
static Transform perspective(Float fov, Float clipNear, Float clipFar);
|
|
|
|
/** \brief Create a perspective transformation for OpenGL.
|
|
* (Maps [near, far] to [-1, 1])
|
|
* \param fov Field of view in degrees
|
|
* \param clipNear Near clipping plane distance
|
|
* \param clipFar Far clipping plane distance
|
|
*/
|
|
static Transform glPerspective(Float fov, Float clipNear, Float clipFar);
|
|
|
|
/** \brief Create a perspective transformation for OpenGL.
|
|
* \param left Left clipping plane coordinate
|
|
* \param right Right clipping plane coordinate
|
|
* \param top Top clipping plane coordinate
|
|
* \param bottom Bottom clipping plane coordinate
|
|
* \param nearVal Near clipping plane distance
|
|
* \param farVal Far clipping plane distance
|
|
*/
|
|
static Transform glFrustum(Float left, Float right, Float bottom, Float top, Float nearVal, Float farVal);
|
|
|
|
/** \brief Create an orthographic transformation, which maps Z to [0,1]
|
|
* and leaves the X and Y coordinates untouched.
|
|
* \param clipNear Near clipping plane
|
|
* \param clipFar Far clipping plane
|
|
*/
|
|
static Transform orthographic(Float clipNear, Float clipFar);
|
|
|
|
/** \brief Create an orthographic transformation for OpenGL
|
|
* \param clipNear Near clipping plane
|
|
* \param clipFar Far clipping plane
|
|
*/
|
|
static Transform glOrthographic(Float clipNear, Float clipFar);
|
|
|
|
/** \brief Create a look-at camera transformation
|
|
* \param p Camera position
|
|
* \param t Target vector
|
|
* \param u Up vector
|
|
*/
|
|
static Transform lookAt(const Point &p, const Point &t, const Vector &u);
|
|
|
|
/** \brief Create an orthogonal transformation that takes
|
|
* the standard to the supplied frame
|
|
*/
|
|
static Transform fromFrame(const Frame &frame);
|
|
|
|
/// Serialize a transformation to a stream
|
|
inline void serialize(Stream *stream) const {
|
|
m_transform.serialize(stream);
|
|
m_invTransform.serialize(stream);
|
|
}
|
|
|
|
/// Return a string representation
|
|
std::string toString() const;
|
|
private:
|
|
Matrix4x4 m_transform;
|
|
Matrix4x4 m_invTransform;
|
|
};
|
|
|
|
MTS_NAMESPACE_END
|
|
|
|
#endif /* __TRANSFORM_H */
|