621 lines
17 KiB
C++
621 lines
17 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/>.
|
|
*/
|
|
|
|
#include <mitsuba/core/transform.h>
|
|
#include <mitsuba/core/frame.h>
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Linear transformation class
|
|
// -----------------------------------------------------------------------
|
|
|
|
Transform Transform::operator*(const Transform &t) const {
|
|
return Transform(m_transform * t.m_transform,
|
|
t.m_invTransform * m_invTransform);
|
|
}
|
|
|
|
Transform Transform::translate(const Vector &v) {
|
|
Matrix4x4 trafo(
|
|
1, 0, 0, v.x,
|
|
0, 1, 0, v.y,
|
|
0, 0, 1, v.z,
|
|
0, 0, 0, 1
|
|
);
|
|
Matrix4x4 invTrafo(
|
|
1, 0, 0, -v.x,
|
|
0, 1, 0, -v.y,
|
|
0, 0, 1, -v.z,
|
|
0, 0, 0, 1
|
|
);
|
|
return Transform(trafo, invTrafo);
|
|
}
|
|
|
|
Transform Transform::scale(const Vector &v) {
|
|
Matrix4x4 trafo(
|
|
v.x, 0, 0, 0,
|
|
0, v.y, 0, 0,
|
|
0, 0, v.z, 0,
|
|
0, 0, 0, 1
|
|
);
|
|
Matrix4x4 invTrafo(
|
|
1.0f/v.x, 0, 0, 0,
|
|
0, 1.0f/v.y, 0, 0,
|
|
0, 0, 1.0f/v.z, 0,
|
|
0, 0, 0, 1
|
|
);
|
|
return Transform(trafo, invTrafo);
|
|
}
|
|
|
|
Transform Transform::rotate(const Vector &axis, Float angle) {
|
|
Float sinTheta, cosTheta;
|
|
|
|
/* Make sure that the axis is normalized */
|
|
Vector naxis = normalize(axis);
|
|
math::sincos(degToRad(angle), &sinTheta, &cosTheta);
|
|
|
|
Matrix4x4 result;
|
|
result(0, 0) = naxis.x * naxis.x + (1.0f - naxis.x * naxis.x) * cosTheta;
|
|
result(0, 1) = naxis.x * naxis.y * (1.0f - cosTheta) - naxis.z * sinTheta;
|
|
result(0, 2) = naxis.x * naxis.z * (1.0f - cosTheta) + naxis.y * sinTheta;
|
|
result(0, 3) = 0;
|
|
|
|
result(1, 0) = naxis.x * naxis.y * (1.0f - cosTheta) + naxis.z * sinTheta;
|
|
result(1, 1) = naxis.y * naxis.y + (1.0f - naxis.y * naxis.y) * cosTheta;
|
|
result(1, 2) = naxis.y * naxis.z * (1.0f - cosTheta) - naxis.x * sinTheta;
|
|
result(1, 3) = 0;
|
|
|
|
result(2, 0) = naxis.x * naxis.z * (1.0f - cosTheta) - naxis.y * sinTheta;
|
|
result(2, 1) = naxis.y * naxis.z * (1.0f - cosTheta) + naxis.x * sinTheta;
|
|
result(2, 2) = naxis.z * naxis.z + (1.0f - naxis.z * naxis.z) * cosTheta;
|
|
result(2, 3) = 0;
|
|
|
|
result(3, 0) = 0;
|
|
result(3, 1) = 0;
|
|
result(3, 2) = 0;
|
|
result(3, 3) = 1;
|
|
|
|
/* The matrix is orthonormal */
|
|
Matrix4x4 transp;
|
|
result.transpose(transp);
|
|
return Transform(result, transp);
|
|
}
|
|
|
|
Transform Transform::perspective(Float fov, Float clipNear, Float clipFar) {
|
|
/* Project vectors in camera space onto a plane at z=1:
|
|
*
|
|
* xProj = x / z
|
|
* yProj = y / z
|
|
* zProj = (far * (z - near)) / (z * (far-near))
|
|
*
|
|
* Camera-space depths are not mapped linearly!
|
|
*/
|
|
Float recip = 1.0f / (clipFar - clipNear);
|
|
|
|
/* Perform a scale so that the field of view is mapped
|
|
* to the interval [-1, 1] */
|
|
Float cot = 1.0f / std::tan(degToRad(fov / 2.0f));
|
|
|
|
Matrix4x4 trafo(
|
|
cot, 0, 0, 0,
|
|
0, cot, 0, 0,
|
|
0, 0, clipFar * recip, -clipNear * clipFar * recip,
|
|
0, 0, 1, 0
|
|
);
|
|
|
|
|
|
return Transform(trafo);
|
|
}
|
|
|
|
Transform Transform::glPerspective(Float fov, Float clipNear, Float clipFar) {
|
|
Float recip = 1.0f / (clipNear - clipFar);
|
|
Float cot = 1.0f / std::tan(degToRad(fov / 2.0f));
|
|
|
|
Matrix4x4 trafo(
|
|
cot, 0, 0, 0,
|
|
0, cot, 0, 0,
|
|
0, 0, (clipFar + clipNear) * recip, 2 * clipFar * clipNear * recip,
|
|
0, 0, -1, 0
|
|
);
|
|
|
|
return Transform(trafo);
|
|
}
|
|
|
|
Transform Transform::glFrustum(Float left, Float right, Float bottom, Float top, Float nearVal, Float farVal) {
|
|
Float invFMN = 1 / (farVal-nearVal);
|
|
Float invTMB = 1 / (top-bottom);
|
|
Float invRML = 1 / (right-left);
|
|
|
|
Matrix4x4 trafo(
|
|
2*nearVal*invRML, 0, (right+left)*invRML, 0,
|
|
0, 2*nearVal*invTMB, (top+bottom)*invTMB, 0,
|
|
0, 0, -(farVal + nearVal) * invFMN, -2*farVal*nearVal*invFMN,
|
|
0, 0, -1, 0
|
|
);
|
|
|
|
return Transform(trafo);
|
|
}
|
|
|
|
Transform Transform::orthographic(Float clipNear, Float clipFar) {
|
|
return scale(Vector(1.0f, 1.0f, 1.0f / (clipFar - clipNear))) *
|
|
translate(Vector(0.0f, 0.0f, -clipNear));
|
|
}
|
|
|
|
Transform Transform::glOrthographic(Float clipNear, Float clipFar) {
|
|
Float a = -2.0f / (clipFar - clipNear),
|
|
b = -(clipFar + clipNear) / (clipFar - clipNear);
|
|
|
|
Matrix4x4 trafo(
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, a, b,
|
|
0, 0, 0, 1
|
|
);
|
|
return Transform(trafo);
|
|
}
|
|
|
|
Transform Transform::glOrthographic(Float clipLeft, Float clipRight,
|
|
Float clipBottom, Float clipTop, Float clipNear, Float clipFar) {
|
|
Float fx = 2.0f / (clipRight - clipLeft),
|
|
fy = 2.0f / (clipTop - clipBottom),
|
|
fz = -2.0f / (clipFar - clipNear),
|
|
tx = -(clipRight + clipLeft) / (clipRight - clipLeft),
|
|
ty = -(clipTop + clipBottom) / (clipTop - clipBottom),
|
|
tz = -(clipFar + clipNear) / (clipFar - clipNear);
|
|
|
|
Matrix4x4 trafo(
|
|
fx, 0, 0, tx,
|
|
0, fy, 0, ty,
|
|
0, 0, fz, tz,
|
|
0, 0, 0, 1
|
|
);
|
|
|
|
return Transform(trafo);
|
|
}
|
|
|
|
Transform Transform::lookAt(const Point &p, const Point &t, const Vector &up) {
|
|
Vector dir = normalizeStrict(t-p, "lookAt(): 'origin' and 'target' coincide!");
|
|
Vector left = normalizeStrict(cross(up, dir), "lookAt(): the forward and upward direction must be linearly independent!");
|
|
Vector newUp = cross(dir, left);
|
|
|
|
Matrix4x4 result, inverse;
|
|
result(0, 0) = left.x; result(1, 0) = left.y; result(2, 0) = left.z; result(3, 0) = 0;
|
|
result(0, 1) = newUp.x; result(1, 1) = newUp.y; result(2, 1) = newUp.z; result(3, 1) = 0;
|
|
result(0, 2) = dir.x; result(1, 2) = dir.y; result(2, 2) = dir.z; result(3, 2) = 0;
|
|
result(0, 3) = p.x; result(1, 3) = p.y; result(2, 3) = p.z; result(3, 3) = 1;
|
|
|
|
/* The inverse is simple to compute for this matrix, so do it directly here */
|
|
Vector q(
|
|
result(0, 0) * p.x + result(1, 0) * p.y + result(2, 0) * p.z,
|
|
result(0, 1) * p.x + result(1, 1) * p.y + result(2, 1) * p.z,
|
|
result(0, 2) * p.x + result(1, 2) * p.y + result(2, 2) * p.z);
|
|
|
|
inverse(0, 0) = left.x; inverse(1, 0) = newUp.x; inverse(2, 0) = dir.x; inverse(3, 0) = 0;
|
|
inverse(0, 1) = left.y; inverse(1, 1) = newUp.y; inverse(2, 1) = dir.y; inverse(3, 1) = 0;
|
|
inverse(0, 2) = left.z; inverse(1, 2) = newUp.z; inverse(2, 2) = dir.z; inverse(3, 2) = 0;
|
|
inverse(0, 3) = -q.x; inverse(1, 3) = -q.y; inverse(2, 3) = -q.z; inverse(3, 3) = 1;
|
|
|
|
return Transform(result, inverse);
|
|
}
|
|
|
|
Transform Transform::fromFrame(const Frame &frame) {
|
|
Matrix4x4 result(
|
|
frame.s.x, frame.t.x, frame.n.x, 0,
|
|
frame.s.y, frame.t.y, frame.n.y, 0,
|
|
frame.s.z, frame.t.z, frame.n.z, 0,
|
|
0, 0, 0, 1
|
|
);
|
|
/* The matrix is orthonormal */
|
|
Matrix4x4 transp;
|
|
result.transpose(transp);
|
|
return Transform(result, transp);
|
|
}
|
|
|
|
std::string Transform::toString() const {
|
|
return m_transform.toString();
|
|
}
|
|
|
|
/**
|
|
* \brief Tridiagonalizes a 3x3 matrix using a single
|
|
* Householder transformation and returns the result.
|
|
*
|
|
* Based on "Geometric Tools" by David Eberly.
|
|
*/
|
|
static inline void tred3(Matrix3x3 &m, Float *diag, Float *subd) {
|
|
Float m00 = m(0, 0), m01 = m(0, 1), m02 = m(0, 2),
|
|
m11 = m(1, 1), m12 = m(1, 2), m22 = m(2, 2);
|
|
|
|
diag[0] = m00;
|
|
subd[2] = 0;
|
|
if (std::abs(m02) > std::numeric_limits<Float>::epsilon()) {
|
|
Float length = std::sqrt(m01*m01 + m02*m02),
|
|
invLength = 1 / length;
|
|
m01 *= invLength;
|
|
m02 *= invLength;
|
|
Float q = 2*m01*m12 + m02*(m22 - m11);
|
|
diag[1] = m11 + m02*q;
|
|
diag[2] = m22 - m02*q;
|
|
subd[0] = length;
|
|
subd[1] = m12 - m01*q;
|
|
m = Matrix3x3(
|
|
1, 0, 0,
|
|
0, m01, m02,
|
|
0, m02, -m01);
|
|
} else {
|
|
/* The matrix is already tridiagonal,
|
|
return an identity transformation matrix */
|
|
diag[1] = m11;
|
|
diag[2] = m22;
|
|
subd[0] = m01;
|
|
subd[1] = m12;
|
|
m = Matrix3x3(
|
|
1, 0, 0,
|
|
0, 1, 0,
|
|
0, 0, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute the eigenvalues and eigenvectors of a 3x3 symmetric tridiagonal
|
|
* matrix using the QL algorithm with implicit shifts
|
|
*
|
|
* Based on "Geometric Tools" by David Eberly and Numerical Recipes by
|
|
* Teukolsky, et al. Originally, this code goes back to the Handbook
|
|
* for Automatic Computation by Wilkionson and Reinsch, as well as
|
|
* the corresponding EISPACK routine.
|
|
*/
|
|
|
|
static inline bool ql3(Matrix3x3 &m, Float *diag, Float *subd) {
|
|
const int maxIter = 32;
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
int k;
|
|
for (k = 0; k < maxIter; ++k) {
|
|
int j;
|
|
for (j = i; j < 2; ++j) {
|
|
Float tmp = std::abs(diag[j]) + std::abs(diag[j+1]);
|
|
if (std::abs(subd[j]) + tmp == tmp)
|
|
break;
|
|
}
|
|
if (j == i)
|
|
break;
|
|
|
|
Float value0 = (diag[i + 1] - diag[i])/(2*subd[i]);
|
|
Float value1 = std::sqrt(value0*value0 + 1);
|
|
value0 = diag[j] - diag[i] + subd[i] /
|
|
((value0 < 0) ? (value0 - value1) : (value0 + value1));
|
|
|
|
Float sn = 1, cs = 1, value2 = 0;
|
|
for (int l = j - 1; l >= i; --l) {
|
|
Float value3 = sn*subd[l], value4 = cs*subd[l];
|
|
if (std::abs(value3) >= std::abs(value0)) {
|
|
cs = value0 / value3;
|
|
value1 = std::sqrt(cs*cs + 1);
|
|
subd[l + 1] = value3*value1;
|
|
sn = 1.0f / value1;
|
|
cs *= sn;
|
|
} else {
|
|
sn = value3 / value0;
|
|
value1 = std::sqrt(sn*sn + 1);
|
|
subd[l + 1] = value0*value1;
|
|
cs = 1.0f / value1;
|
|
sn *= cs;
|
|
}
|
|
value0 = diag[l + 1] - value2;
|
|
value1 = (diag[l] - value0)*sn + 2*value4*cs;
|
|
value2 = sn*value1;
|
|
diag[l + 1] = value0 + value2;
|
|
value0 = cs*value1 - value4;
|
|
|
|
for (int t = 0; t < 3; ++t) {
|
|
value3 = m(t, l + 1);
|
|
m(t, l + 1) = sn*m(t, l) + cs*value3;
|
|
m(t, l) = cs*m(t, l) - sn*value3;
|
|
}
|
|
}
|
|
diag[i] -= value2;
|
|
subd[i] = value0;
|
|
subd[j] = 0;
|
|
}
|
|
if (k == maxIter)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Fast 3x3 eigenvalue decomposition
|
|
bool eig3(Matrix3x3 &m, Float lambda[3]) {
|
|
Float subd[3];
|
|
|
|
/* Reduce to Hessenberg form */
|
|
tred3(m, lambda, subd);
|
|
|
|
/* Iteratively find the eigenvalues and eigenvectors
|
|
using implicitly shifted QL iterations */
|
|
if (!ql3(m, lambda, subd))
|
|
return false;
|
|
|
|
/* Sort the eigenvalues in decreasing order */
|
|
for (int i=0; i<2; ++i) {
|
|
/* Locate the maximum eigenvalue. */
|
|
int largest = i;
|
|
Float maxValue = lambda[largest];
|
|
for (int j = i+1; j<3; ++j) {
|
|
if (lambda[j] > maxValue) {
|
|
largest = j;
|
|
maxValue = lambda[largest];
|
|
}
|
|
}
|
|
|
|
if (largest != i) {
|
|
// Swap the eigenvalues.
|
|
std::swap(lambda[i], lambda[largest]);
|
|
|
|
// Swap the eigenvectors
|
|
for (int j=0; j<3; ++j)
|
|
std::swap(m(j, i), m(j, largest));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Compute the roots of the cubic polynomial. Double-precision arithmetic
|
|
* is used to minimize the effects due to subtractive cancellation. The
|
|
* roots are returned in increasing order.\
|
|
*
|
|
* Based on "Geometric Tools" by David Eberly.
|
|
*/
|
|
static void eig3_evals(const Matrix3x3 &A, double root[3]) {
|
|
const double msInv3 = 1.0 / 3.0, msRoot3 = std::sqrt(3.0);
|
|
|
|
// Convert the unique matrix entries to double precision.
|
|
double a00 = (double) A(0, 0), a01 = (double) A(0, 1), a02 = (double) A(0, 2),
|
|
a11 = (double) A(1, 1), a12 = (double) A(1, 2), a22 = (double) A(2, 2);
|
|
|
|
// The characteristic equation is x^3 - c2*x^2 + c1*x - c0 = 0. The
|
|
// eigenvalues are the roots to this equation, all guaranteed to be
|
|
// real-valued, because the matrix is symmetric.
|
|
double c0 = a00*a11*a22 + 2.0*a01*a02*a12 - a00*a12*a12 -
|
|
a11*a02*a02 - a22*a01*a01;
|
|
|
|
double c1 = a00*a11 - a01*a01 + a00*a22 - a02*a02 +
|
|
a11*a22 - a12*a12;
|
|
|
|
double c2 = a00 + a11 + a22;
|
|
|
|
// Construct the parameters used in classifying the roots of the equation
|
|
// and in solving the equation for the roots in closed form.
|
|
double c2Div3 = c2*msInv3;
|
|
double aDiv3 = (c1 - c2*c2Div3)*msInv3;
|
|
if (aDiv3 > 0.0)
|
|
aDiv3 = 0.0;
|
|
|
|
double halfMB = 0.5*(c0 + c2Div3*(2.0*c2Div3*c2Div3 - c1));
|
|
|
|
double q = halfMB*halfMB + aDiv3*aDiv3*aDiv3;
|
|
if (q > 0.0)
|
|
q = 0.0;
|
|
|
|
// Compute the eigenvalues by solving for the roots of the polynomial.
|
|
double magnitude = std::sqrt(-aDiv3);
|
|
double angle = std::atan2(std::sqrt(-q), halfMB)*msInv3;
|
|
double cs = std::cos(angle);
|
|
double sn = std::sin(angle);
|
|
double root0 = c2Div3 + 2.0*magnitude*cs;
|
|
double root1 = c2Div3 - magnitude*(cs + msRoot3*sn);
|
|
double root2 = c2Div3 - magnitude*(cs - msRoot3*sn);
|
|
|
|
// Sort in increasing order.
|
|
if (root1 <= root0) {
|
|
root[0] = root0;
|
|
root[1] = root1;
|
|
} else {
|
|
root[0] = root1;
|
|
root[1] = root0;
|
|
}
|
|
|
|
if (root2 <= root[1]) {
|
|
root[2] = root2;
|
|
} else {
|
|
root[2] = root[1];
|
|
if (root2 <= root[0]) {
|
|
root[1] = root2;
|
|
} else {
|
|
root[1] = root[0];
|
|
root[0] = root2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determine if M has positive rank. The maximum-magnitude entry of M is
|
|
* returned. The row in which it is contained is also returned.
|
|
*
|
|
* Based on "Geometric Tools" by David Eberly.
|
|
*/
|
|
static bool eig3_rank(const Matrix3x3 &m, Float& maxEntry, Vector &maxRow) {
|
|
// Locate the maximum-magnitude entry of the matrix.
|
|
maxEntry = -1.0f;
|
|
int maxRowIndex = -1;
|
|
for (int row = 0; row < 3; ++row) {
|
|
for (int col = row; col < 3; ++col) {
|
|
Float absValue = std::abs(m(row, col));
|
|
if (absValue > maxEntry) {
|
|
maxEntry = absValue;
|
|
maxRowIndex = row;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the row containing the maximum, to be used for eigenvector
|
|
// construction.
|
|
maxRow = m.row(maxRowIndex);
|
|
|
|
return maxEntry >= std::numeric_limits<Float>::epsilon();
|
|
}
|
|
|
|
/**
|
|
* Compute the eigenvectors
|
|
*
|
|
* Based on "Geometric Tools" by David Eberly.
|
|
*/
|
|
static void eig3_evecs(Matrix3x3& A, const Float lambda[3], const Vector& U2, int i0, int i1, int i2) {
|
|
Vector U0, U1;
|
|
coordinateSystem(normalize(U2), U0, U1);
|
|
|
|
// V[i2] = c0*U0 + c1*U1, c0^2 + c1^2=1
|
|
// e2*V[i2] = c0*A*U0 + c1*A*U1
|
|
// e2*c0 = c0*U0.Dot(A*U0) + c1*U0.Dot(A*U1) = d00*c0 + d01*c1
|
|
// e2*c1 = c0*U1.Dot(A*U0) + c1*U1.Dot(A*U1) = d01*c0 + d11*c1
|
|
Vector tmp = A*U0, evecs[3];
|
|
|
|
Float p00 = lambda[i2] - dot(U0, tmp),
|
|
p01 = dot(U1, tmp),
|
|
p11 = lambda[i2] - dot(U1, A*U1),
|
|
maxValue = std::abs(p00),
|
|
absValue = std::abs(p01),
|
|
invLength;
|
|
|
|
if (absValue > maxValue)
|
|
maxValue = absValue;
|
|
|
|
int row = 0;
|
|
absValue = std::abs(p11);
|
|
if (absValue > maxValue) {
|
|
maxValue = absValue;
|
|
row = 1;
|
|
}
|
|
|
|
if (maxValue >= std::numeric_limits<Float>::epsilon()) {
|
|
if (row == 0) {
|
|
invLength = 1/std::sqrt(p00*p00 + p01*p01);
|
|
p00 *= invLength;
|
|
p01 *= invLength;
|
|
evecs[i2] = p01*U0 + p00*U1;
|
|
} else {
|
|
invLength = 1/std::sqrt(p11*p11 + p01*p01);
|
|
p11 *= invLength;
|
|
p01 *= invLength;
|
|
evecs[i2] = p11*U0 + p01*U1;
|
|
}
|
|
} else {
|
|
if (row == 0)
|
|
evecs[i2] = U1;
|
|
else
|
|
evecs[i2] = U0;
|
|
}
|
|
|
|
// V[i0] = c0*U2 + c1*Cross(U2,V[i2]) = c0*R + c1*S
|
|
// e0*V[i0] = c0*A*R + c1*A*S
|
|
// e0*c0 = c0*R.Dot(A*R) + c1*R.Dot(A*S) = d00*c0 + d01*c1
|
|
// e0*c1 = c0*S.Dot(A*R) + c1*S.Dot(A*S) = d01*c0 + d11*c1
|
|
Vector S = cross(U2, evecs[i2]);
|
|
tmp = A*U2;
|
|
p00 = lambda[i0] - dot(U2, tmp);
|
|
p01 = dot(S, tmp);
|
|
p11 = lambda[i0] - dot(S, A*S);
|
|
maxValue = std::abs(p00);
|
|
row = 0;
|
|
absValue = std::abs(p01);
|
|
if (absValue > maxValue)
|
|
maxValue = absValue;
|
|
absValue = std::abs(p11);
|
|
if (absValue > maxValue) {
|
|
maxValue = absValue;
|
|
row = 1;
|
|
}
|
|
|
|
if (maxValue >= std::numeric_limits<Float>::epsilon()) {
|
|
if (row == 0) {
|
|
invLength = 1/std::sqrt(p00*p00 + p01*p01);
|
|
p00 *= invLength;
|
|
p01 *= invLength;
|
|
evecs[i0] = p01*U2 + p00*S;
|
|
} else {
|
|
invLength = 1/std::sqrt(p11*p11 + p01*p01);
|
|
p11 *= invLength;
|
|
p01 *= invLength;
|
|
evecs[i0] = p11*U2 + p01*S;
|
|
}
|
|
} else {
|
|
if (row == 0)
|
|
evecs[i0] = S;
|
|
else
|
|
evecs[i0] = U2;
|
|
}
|
|
|
|
evecs[i1] = cross(evecs[i2], evecs[i0]);
|
|
A = Matrix3x3(
|
|
evecs[0].x, evecs[1].x, evecs[2].x,
|
|
evecs[0].y, evecs[1].y, evecs[2].y,
|
|
evecs[0].z, evecs[1].z, evecs[2].z
|
|
);
|
|
}
|
|
|
|
/**
|
|
* \brief Fast non-iterative 3x3 eigenvalue decomposition
|
|
*
|
|
* Based on "Geometric Tools" by David Eberly.
|
|
*/
|
|
void eig3_noniter(Matrix3x3 &A, Float lambda[3]) {
|
|
// Compute the eigenvalues using double-precision arithmetic.
|
|
double root[3];
|
|
eig3_evals(A, root);
|
|
lambda[0] = (Float) root[0];
|
|
lambda[1] = (Float) root[1];
|
|
lambda[2] = (Float) root[2];
|
|
Matrix3x3 eigs;
|
|
|
|
Float maxEntry[3];
|
|
Vector maxRow[3];
|
|
for (int i = 0; i < 3; ++i) {
|
|
Matrix3x3 M(A);
|
|
M(0, 0) -= lambda[i];
|
|
M(1, 1) -= lambda[i];
|
|
M(2, 2) -= lambda[i];
|
|
if (!eig3_rank(M, maxEntry[i], maxRow[i])) {
|
|
A.setIdentity();
|
|
return;
|
|
}
|
|
}
|
|
|
|
Float totalMax = maxEntry[0];
|
|
int i = 0;
|
|
if (maxEntry[1] > totalMax) {
|
|
totalMax = maxEntry[1];
|
|
i = 1;
|
|
} if (maxEntry[2] > totalMax) {
|
|
i = 2;
|
|
}
|
|
|
|
if (i == 0) {
|
|
maxRow[0] = normalize(maxRow[0]);
|
|
eig3_evecs(A, lambda, maxRow[0], 1, 2, 0);
|
|
} else if (i == 1) {
|
|
maxRow[1] = normalize(maxRow[1]);
|
|
eig3_evecs(A, lambda, maxRow[1], 2, 0, 1);
|
|
} else {
|
|
maxRow[2] = normalize(maxRow[2]);
|
|
eig3_evecs(A, lambda, maxRow[2], 0, 1, 2);
|
|
}
|
|
}
|
|
|
|
MTS_NAMESPACE_END
|