GRIM: checked, cleaned, and moved Euler calculations to rotation3d.h

This commit is contained in:
Thomas Allen 2012-03-06 02:25:12 -05:00 committed by Giulio Camuffo
parent 67e7a05119
commit 8484d7870e
2 changed files with 52 additions and 84 deletions

View File

@ -54,22 +54,6 @@ void Head::setMaxAngles(float maxPitch, float maxYaw, float maxRoll) {
_maxYaw = maxYaw;
}
void setCol(Math::Matrix4 &m, int col, const Math::Vector3d &vec)
{
m.setValue(0, col, vec.x());
m.setValue(1, col, vec.y());
m.setValue(2, col, vec.z());
m.setValue(3, col, col == 3 ? 1.f : 0.f);
}
void setRow(Math::Matrix4 &m, int row, const Math::Vector3d &vec)
{
m.setValue(row, 0, vec.x());
m.setValue(row, 1, vec.y());
m.setValue(row, 2, vec.z());
m.setValue(row, 3, row == 3 ? 1.f : 0.f);
}
/**
* Generates a lookat matrix with position at origin. For reference, see
* http://clb.demon.fi/MathGeoLib/docs/float3x3_LookAt.php
@ -84,59 +68,24 @@ Math::Matrix4 lookAtMatrix(const Math::Vector3d &localForward, const Math::Vecto
Math::Vector3d perpWorldUp = Math::Vector3d::crossProduct(targetDirection, worldRight);
perpWorldUp.normalize();
Math::Matrix4 m1;
setCol(m1, 0, worldRight);
setCol(m1, 1, perpWorldUp);
setCol(m1, 2, targetDirection);
setCol(m1, 3, Math::Vector3d(0,0,0));
Math::Matrix3 m1;
m1.getRow(0) << worldRight.x() << worldRight.y() << worldRight.z();
m1.getRow(1) << perpWorldUp.x() << perpWorldUp.y() << perpWorldUp.z();
m1.getRow(2) << targetDirection.x() << targetDirection.y() << targetDirection.z();
m1.transpose();
Math::Matrix4 m2;
setRow(m2, 0, localRight);
setRow(m2, 1, localUp);
setRow(m2, 2, localForward);
setRow(m2, 3, Math::Vector3d(0,0,0));
Math::Matrix3 m2;
m2.getRow(0) << localRight.x() << localRight.y() << localRight.z();
m2.getRow(1) << localUp.x() << localUp.y() << localUp.z();
m2.getRow(2) << localForward.x() << localForward.y() << localForward.z();
return m1 * m2;
Math::Matrix4 m3;
m3.setToIdentity();
m3.setRotation(m1 * m2);
return m3;
}
/**
* Decomposes the matrix M to form M = R_z * R_x * R_y (R_D being the cardinal rotation
* matrix about the axis +D), and outputs the angles of rotation in parameters Z, X and Y.
* In the convention of the coordinate system used in Grim Fandango characters:
* +Z is the yaw rotation (up axis)
* +X is the pitch rotation (right axis)
* +Y is the roll rotation (forward axis)
* This function was adapted from http://www.geometrictools.com/Documentation/EulerAngles.pdf
* The matrix M must be orthonormal.
*/
void extractEulerZXY(const Math::Matrix4 &m, Math::Angle &Z, Math::Angle &X, Math::Angle &Y) {
float x,y,z;
if (m.getValue(2, 1) < 1.f) {
if (m.getValue(2, 1) > -1.f) {
x = asin(m.getValue(2, 1));
z = atan2(-m.getValue(0, 1), m.getValue(1, 1));
y = atan2(-m.getValue(2, 0), m.getValue(2, 2));
}
else {
// Not a unique solution. Pick an arbitrary one.
x = -3.141592654f/2.f;
z = -atan2(-m.getValue(0, 2), m.getValue(0, 0));
y = 0;
}
}
else {
// Not a unique solution. Pick an arbitrary one.
x = 3.141592654f/2.f;
z = atan2(m.getValue(0, 2), m.getValue(0, 0));
y = 0;
}
X = Math::Angle::fromRadians(x);
Y = Math::Angle::fromRadians(y);
Z = Math::Angle::fromRadians(z);
}
void Head::lookAt(bool entering, const Math::Vector3d &point, float rate, const Math::Matrix4 &matrix, const Common::String &fname) {
if (_joint1Node) {
@ -197,10 +146,10 @@ void Head::lookAt(bool entering, const Math::Vector3d &point, float rate, const
// orientation.
Math::Matrix4 lookAtTM;
const Math::Vector3d worldUp(0,0,1); // The Residual scene convention: +Z is world space up.
if (Math::Vector3d::dotProduct(v, Math::Vector3d(0,0,1)) >= 0.98f) // Avoid singularity if trying to look straight up.
if (Math::Vector3d::dotProduct(v, worldUp) >= 0.98f) // Avoid singularity if trying to look straight up.
lookAtTM = lookAtMatrix(localFront, v, localUp, -frontDir); // Instead of orienting head towards scene up, orient head towards character "back",
// i.e. when you look straight up, your head up vector tilts/arches to point straight backwards.
else if (Math::Vector3d::dotProduct(v, Math::Vector3d(0,0,1)) <= -0.98f) // Avoid singularity if trying to look straight down.
else if (Math::Vector3d::dotProduct(v, worldUp) <= -0.98f) // Avoid singularity if trying to look straight down.
lookAtTM = lookAtMatrix(localFront, v, localUp, frontDir); // Instead of orienting head towards scene down, orient head towards character "front",
// i.e. when you look straight down, your head up vector tilts/arches to point straight forwards.
else
@ -229,7 +178,7 @@ void Head::lookAt(bool entering, const Math::Vector3d &point, float rate, const
// Decompose to yaw-pitch-roll (+Z, +X, +Y).
// In this space, Yaw is +Z. Pitch is +X. Roll is +Y.
Math::Angle y, pt, r;
extractEulerZXY(lookAtTM, y, pt, r);
lookAtTM.getPitchYawRoll(&pt, &y, &r);
// Constrain the maximum head movement, as desired by the game LUA scripts.
y.clampDegrees(_maxYaw);
@ -283,7 +232,7 @@ void Head::lookAt(bool entering, const Math::Vector3d &point, float rate, const
// be deleted, and this function can simply output the contents of pt, y and r variables above.
lookAtTM = animFrame * lookAtTM;
extractEulerZXY(lookAtTM, y, pt, r);
lookAtTM.getPitchYawRoll(&pt, &y, &r);
_joint3Node->_animYaw = y;
_joint3Node->_animPitch = pt;
_joint3Node->_animRoll = r;

View File

@ -104,25 +104,44 @@ void Rotation3D<T>::buildAroundYaw(const Angle &yaw) {
this->getMatrix().getRow(2) << 0.f << 0.f << 1.f;
}
/**
* Decomposes the matrix M to form M = R_z * R_x * R_y (R_D being the cardinal rotation
* matrix about the axis +D), and outputs the angles of rotation in parameters pPitch, pYaw and pRoll.
* In the convention of the coordinate system used in Grim Fandango characters:
* Pitch is rotation about the X axis (right)
* Yaw is rotation about the Z axis (up)
* Roll is rotation about the Y axix (out)
* This function was adapted from http://www.geometrictools.com/Documentation/EulerAngles.pdf
* The matrix M must be orthonormal.
*/
template<class T>
void Rotation3D<T>::getPitchYawRoll(Angle *pPitch, Angle *pYaw, Angle *pRoll) const {
// based on http://planning.cs.uiuc.edu/node103.html
if (pYaw) {
*pYaw = Angle::arcTangent2(this->getMatrix().getValue(1, 0),
this->getMatrix().getValue(0, 0));
const T *m = &(this->getMatrix()); // so dumb
float x,y,z;
if (m->getValue(2, 1) < 1.f) {
if (m->getValue(2, 1) > -1.f) {
x = asin(m->getValue(2, 1));
z = atan2(-m->getValue(0, 1), m->getValue(1, 1));
y = atan2(-m->getValue(2, 0), m->getValue(2, 2));
}
else {
// Not a unique solution. Pick an arbitrary one.
x = -3.141592654f/2.f;
z = -atan2(-m->getValue(0, 2), m->getValue(0, 0));
y = 0;
}
}
if (pRoll) {
float a = this->getMatrix().getValue(2, 1);
float b = this->getMatrix().getValue(2, 2);
float mag = sqrt(a * a + b * b);
*pRoll = Angle::arcTangent2(-this->getMatrix().getValue(2, 0), mag);
}
if (pPitch) {
*pPitch = Angle::arcTangent2(this->getMatrix().getValue(2, 1),
this->getMatrix().getValue(2, 2));
else {
// Not a unique solution. Pick an arbitrary one.
x = 3.141592654f/2.f;
z = atan2(m->getValue(0, 2), m->getValue(0, 0));
y = 0;
}
*pPitch = Math::Angle::fromRadians(x);
*pRoll = Math::Angle::fromRadians(y);
*pYaw = Math::Angle::fromRadians(z);
}
template<class T>