/***********************************************************************************************
*
* Copyright © DreamWorks Interactive. 1996
*
* Contents:
* CRotate3
* CPlacement3
*
* Bugs:
*
* To do:
* Incorporate fast sqrt routines when they have enough precision.
* Optimise conversion from Euler angles.
*
***********************************************************************************************
*
* $Log:: /JP2_PC/Source/Lib/Transform5/Rotate.hpp $
*
* 26 97-04-24 18:40 Speter
* Folded new changes from 4.2 version into this 5.0 specific version.
*
* 31 97-04-23 14:28 Speter
* Changed tolerance for normalisation asserts (sorry).
*
* 30 97-04-22 10:50 Speter
* Sped up Dir * Rotate function by avoiding dir renormalisation.
*
* 29 4/16/97 2:24p Agrant
* Restored SFrame to allow CRotate --> CMatrix conversion
*
* 28 97/03/24 14:54 Speter
* Removed constructors of CDirs and CRotates from ASCII chars; use d3ZAxis etc. constants.
* When we need optimisation for axes, we'll use derived classes which don't need a switch
* statement.
*
* 27 97/02/19 17:54 Speter
* Made direct constructors public.
*
* 26 97/02/07 19:15 Speter
* Removed SFrame3, associated CRotate3 functions, as they were inefficient and were replaced
* with equivalent CMatrix functions. Removed CMatrix3 to CRotate3 conversion, as it used
* SFrame3 and wasn't used.
*
* 25 97/01/26 20:00 Speter
* Added operator to interpolate rotation by a scalar.
* Sped up rotate vector, rotation to matrix, and rotation from vector operations.
* Aligned code.
*
* 24 12/20/96 3:52p Mlange
* Moved the CRotate3 constructor that takes a character axis and angle to the class
* declaration in the header file.
*
* 23 96/12/10 11:06 Speter
* Upped fMAX_DENORMALISATION to .01 from .0001.
*
* 22 12/09/96 8:46p Agrant
* Added assert in constructor for CRotate taking a CQuaternion
*
* 21 12/09/96 12:43p Agrant
* Removed the 4 TR constructor, paid 5 bucks.
*
* 20 12/06/96 9:51p Agrant
* Hacks to CRotate for physics integration.
* It turns out that we have two different Quaternions.
* Will fix it up soon.
*
* 19 96/12/04 20:19 Speter
* Changed v3T to v3Pos in all transforms. Changed r3T to r3Rot.
* Changed CPlacement3(CTranslate3) to CPlacement3(CVector3).
*
*
*
* 18 96/11/20 11:52 Speter
* Added constructor for CRotate3 from CMatrix3.
*
* 17 96/10/18 19:03 Speter
* Took out include of , now that it's in Common.hpp.
*
* 16 96/09/25 19:50 Speter
* The Big Change.
* In transforms, replaced TReal with TR, TDefReal with TReal.
* Replaced all references to transform templates that have with <>.
* Replaced TObjReal with TReal, and "or" prefix with "r".
* Replaced CObjPoint, CObjNormal, and CObjPlacement with equivalent transform types, and
* prefixes likewise.
* Removed some unnecessary casts to TReal.
* Finally, replaced VER_GENERAL_DEBUG_ASSERTS with VER_DEBUG.
*
* 15 96/09/12 16:27 Speter
* Replaced bFuzzyEquals with Fuzzy ==.
*
* 14 96/09/09 18:32 Speter
* Added SFrame3 structure, and CRotate3 constructor which rotates one frame to another.
*
* 13 96/08/21 18:22 Speter
* Changes from code review:
* Added default template parameter to all classes.
* Made all in-class friends inline.
* Removed CRotate3(char, CAngle) and CRotate3(char*, CAngle, CAngle, CAngle). Replaced with
* CRotate3(uint32, CAngle, CAngle = 0, CAngle = 0), where first param is a multi-byte
* character. Removed 's' and 'r' specifiers for static and rotating frames. Rotating frames
* must now be specified by reversing the order of axes. See notes.
* Added CRotate3 * CDir3 operators, to disambiguate between CPlacement3 * CDir3.
* Changed tLen2() to tLenSqr().
* Changed calculation slightly in Normalise(), added Asserts.
* Added SetOrigin() and AdjustOrigin() functions.
* Added many comments, fixed some prefixes.
*
* 12 96/08/06 18:23 Speter
* Reversed order of d3_to and d3_from in constructor.
* Added CPlacement3 constructor which takes a CTranslate3.
*
* 11 96/08/01 11:04 Speter
* Added automatic renormalisation.
*
* 10 96/07/23 11:01 Speter
* Added static denormalisation variables for debugging.
*
* 9 96/07/12 17:35 Speter
* Added Rotate3 constructor taking a rotation vector.
*
* 8 96/07/08 12:40 Speter
* Changed name of CNormal3 to CDir3 (more general).
* Added specific functions to transform directions, which do not translate positions.
*
* 7 96/06/26 22:07 Speter
* Added a bunch of comments, prettied things up nicely.
*
* 6 96/06/26 17:03 Speter
* Really fixed rotation.
*
* 5 96/06/26 16:43 Speter
* Fixed matrix and vector operations. They were transposed!
*
* 4 96/06/26 13:16 Speter
* Changed TGReal to TR and prefix gr to r.
*
* 3 96/06/25 14:35 Speter
* Finalised design of transform classes, with Position3 and Transform3.
*
* 2 96/06/20 17:13 Speter
* Converted into templates and made necessary changes.
*
* 1 96/06/20 15:26 Speter
* First version of new optimised transform library.
*
**********************************************************************************************/
#ifndef HEADER_LIB_TRANSFORM_ROTATE_HPP
#define HEADER_LIB_TRANSFORM_ROTATE_HPP
#include
#include "Matrix.hpp"
#include "Translate.hpp"
#include "Lib/Math/FastTrig.hpp"
//
// A version flag to enable counting of quaternion multiplications.
// Set to 1 if we ever want to revisit quaternion renormalisation methods.
// (For now, quaternions are checked for denormalisation after every multiply.)
//
#define VER_QUATERNION_COUNT VER_DEBUG
// Threshold at which we renormalise the quaternion.
#define fMAX_QUAT_DENORMALISATION (fMAX_VECTOR_DENORMALISATION * 0.1)
//**********************************************************************************************
//
template struct SFrame3
//
// Prefix: fr3
//
//**************************************
{
CDir3 d3Z, d3Y; // The directions corresponding to the Z and Y axes.
SFrame3()
: d3Z(0,0,1), d3Y(0,1,0)
{
}
SFrame3(const CDir3
& d3_z, const CDir3
& d3_y)
: d3Z(d3_z), d3Y(d3_y)
{
// These directions must be perpendicular!
Assert(Fuzzy(d3Z * d3Y) == 0);
}
};
template
class CQuaternion
{
public:
TR tE0; // Same as tC in CRotate
TR tE1; // X
TR tE2; // Y
TR tE3; // Z
CQuaternion(float e0, float e1, float e2, float e3) : tE0(e0), tE1(e1), tE2(e2), tE3(e3)
{};
CQuaternion()
{};
// CPlacement3(const CRotate3& r3, const CVector3
& v3)
};
//**********************************************************************************************
//
template class CRotate3
//
// Prefix: r3
//
// An arbitrary rotation (any axis and angle).
// Implemented as a quaternion.
//
// Note: Since we use a right-handed coordinate system, an angle of rotation about a vector
// specifies amount of clockwise rotation when looking in the direction of the vector.
//
//**************************************
{
public:
#if VER_DEBUG
// Several static variables for statistics gathering.
static int iMaxNormalisationCount, iAvgNormalisationCount, iMaxCheckCount;
static float fTotalNormalisationCount, fTotalNormalisations;
static int iMaxDenormalisationCount;
static TR tMaxDenormalisation;
#endif
protected:
TR tC; // Scalar part (cosine of half rotation angle).
CVector3 v3S; // Vector part (sine of half rotation angle times vector).
#if VER_QUATERNION_COUNT
int iCount;
#endif
public:
//******************************************************************************************
//
// Constructors.
//
//******************************************************************************************
//
CRotate3()
//
// Identity rotation.
//
//**********************************
: tC((TR)1), v3S((TR)0, (TR)0, (TR)0)
{
InitCounter();
}
//******************************************************************************************
//
CRotate3
(
const CQuaternion<>& q
)
//**********************************
: v3S(q.tE1, q.tE2, q.tE3), tC(q.tE0)
{
Assert(bIsNormalised());
}
//******************************************************************************************
//
CRotate3
(TR t_w, const CVector3
& v3)
//
// Initialise quaternion components directly.
// Invoked by operator~ and .r3Rotate.
//
//**********************************
: tC(t_w), v3S(v3)
{
Assert(bIsNormalised());
}
//******************************************************************************************
//
CRotate3
(
TR t_w, TR t_x, TR t_y, TR t_z
#if VER_QUATERNION_COUNT
,
int i_count
#endif
)
//
// Initialise quaternion components even more directly.
// Invoked by quaternion multiplication operator.
// Therefore, we do renormalisation here.
//
//**********************************
: tC(t_w), v3S(t_x, t_y, t_z)
#if VER_QUATERNION_COUNT
,
iCount(i_count)
#endif
{
Normalise();
#if VER_DEBUG
TR t = Abs(tLenSqr() - (TR)1);
if (t > tMaxDenormalisation)
{
tMaxDenormalisation = t;
iMaxDenormalisationCount = iCount;
}
#endif
}
//******************************************************************************************
CRotate3
(
const CDir3
& d3, // Axis about which to rotate.
CAngle ang // Angle of rotation (clockwise when looking down vector).
);
//**********************************
//******************************************************************************************
CRotate3
(
const CVector3
& v3 // Rotation vector to apply.
// This is a vector whose direction is the axis of rotation,
// and whose magnitude specifies the amount of rotation,
// in radians.
);
//**********************************
//******************************************************************************************
CRotate3
(
const CDir3
& d3_from, // Starting direction.
const CDir3
& d3_to // Destination direction.
);
//
// Constructs a rotation that moves d3_from to d3_to, via the shortest path (great circle).
//
//**********************************
//******************************************************************************************
CRotate3
(
const SFrame3
& fr3_from, // Starting frame.
const SFrame3
& fr3_to // Ending frame.
);
//
// Constructs a rotation that moves the two axes in fr3_from to the corresponding axes
// in fr3_to. Unlike the constructor taking two directions, we do not have to choose an
// arbitrary (great circle) rotation. This rotation is unique.
//
//**********************************
//******************************************************************************************
CRotate3
(
const CMatrix3
& mx3 // A transformation matrix.
);
//
// Constructs a rotation from a matrix.
//
//**********************************
//******************************************************************************************
//
// Conversion operators.
//
// Bug: Due to a confirmed Microsoft bug, conversion operators in template classes
// must be declared inline in the class. That's why the following function is here
// (even though I'd rather put it outside the class).
operator CMatrix3
() const
{
// From Graphics Gems II, Sec VII.6.
// Store products we need more than once.
TR t_cc = tC * tC;
TR t_cx = tC * v3S.tX;
TR t_cy = tC * v3S.tY;
TR t_cz = tC * v3S.tZ;
TR t_xx = v3S.tX * v3S.tX;
TR t_xy = v3S.tX * v3S.tY;
TR t_xz = v3S.tX * v3S.tZ;
TR t_yy = v3S.tY * v3S.tY;
TR t_yz = v3S.tY * v3S.tZ;
TR t_zz = v3S.tZ * v3S.tZ;
return CMatrix3
(
2 * (t_cc + t_xx - 0.5), 2 * (t_xy + t_cz ), 2 * (t_xz - t_cy ),
2 * (t_xy - t_cz ), 2 * (t_cc + t_yy - 0.5), 2 * (t_yz + t_cx ),
2 * (t_xz + t_cy ), 2 * (t_yz - t_cx ), 2 * (t_cc + t_zz - 0.5)
);
}
//******************************************************************************************
//
// Operators.
//
// Return the inverse of the rotate.
CRotate3
operator ~() const
{
// For unit quaternions, the inverse is equal to the conjugate (negated vector part).
return CRotate3
(tC, -v3S);
}
//
// Concatenate with another rotation transform.
//
CRotate3
operator *(const CRotate3
& r3) const;
CRotate3
& operator *=(const CRotate3
& r3)
{
return *this = *this * r3;
}
//
// Interpolate rotation by a parameter. Need not be between 0 and 1.
// Note: For interpolations in a loop, this is slow:
//
// CRotate3<> rot_total;
// CDir3<> d3_orig, d3_new;
// for (float f = 0; f < 1; f += 0.1)
// d3_new = d3_orig * (rot_total * f);
//
// and this is fast:
//
// CRotate3<> rot_total;
// CDir3<> d3_orig;
// CDir3<> d3_new = d3_orig;
// CRotate3<> d3_delta = d3_orig * 0.1;
//
// for (int i = 0; i < 10; i++)
// d3_new *= d3_delta;
//
CRotate3
operator *(TR r_scale);
CRotate3
& operator *=(TR r_scale)
{
return *this = *this * r_scale;
}
//
// Operate on a vector.
//
friend CVector3
operator *(const CVector3
& v3, const CRotate3
& r3);
protected:
//******************************************************************************************
//
// Member functions.
//
//******************************************************************************************
//
TR tLenSqr() const
//
// Returns:
// The square of the length of the quaternion, as if a 4D vector.
//
//**********************************
{
return tC * tC + v3S * v3S;
}
//******************************************************************************************
//
TR tLen() const
//
// Returns:
// Length of the quaternion, as if a 4D vector.
//
//**********************************
{
return (TR) sqrt((double) tLenSqr());
}
//******************************************************************************************
//
bool bIsNormalised() const
//
// Returns:
// Whether the square of the length of the quaternion is fuzzily equal to 1.
//
//**********************************
{
return Fuzzy(tLenSqr(), (TR)fMAX_QUAT_DENORMALISATION) == 1;
}
//******************************************************************************************
//
void Normalise
(
bool b_always = false // Always renormalise without checking.
)
//
// Sets the 4-dimensional quaternion length to 1.
// If b_always is false, check first. This will generally be more efficient,
// as the checking is much faster than the renormalising.
//
//**********************************
{
#if VER_DEBUG
SetMax(iMaxCheckCount, iCount);
#endif
TR t_lensqr = tLenSqr();
// If requested, test whether already approximately normalised.
if (!b_always && bIsNormalised())
return;
#if VER_DEBUG
SetMax(iMaxNormalisationCount, iCount);
fTotalNormalisationCount += iCount;
fTotalNormalisations ++;
iAvgNormalisationCount = (int) (fTotalNormalisationCount / fTotalNormalisations);
#endif
#if VER_QUATERNION_COUNT
iCount = 0;
#endif
//
// We want to use a *particular* sqrt approximation, one
// appropriate to values very close to 1:
//
// sqrt(x) ~= (x+1)/2.
// 1/sqrt(x) ~= 2/(x+1).
//
Assert(Fuzzy(t_lensqr) != 0);
TR t_invlen = (TR)2 / (t_lensqr + (TR)1);
Assert(Fuzzy(t_invlen * t_invlen) == (TR)1 / t_lensqr);
tC *= t_invlen;
v3S *= t_invlen;
Assert(bIsNormalised());
}
//******************************************************************************************
//
void InitCounter()
{
#if VER_QUATERNION_COUNT
iCount = 0;
#endif
}
//
//**********************************
//******************************************************************************************
//
void BumpCounter()
{
#if VER_QUATERNION_COUNT
iCount++;
#endif
}
//
//**********************************
public:
//******************************************************************************************
//
void GetQuaternion
(
CQuaternion
*pq
)
//
//**********************************
{
pq->tE1 = v3S.tX;
pq->tE2 = v3S.tY;
pq->tE3 = v3S.tZ;
pq->tE0 = tC;
}
};
//**********************************************************************************************
//
// Global operators for CRotate3<>.
//
template inline CVector3& operator *=(CVector3
& v3, const CRotate3
& r3)
{
return v3 = v3 * r3;
}
//
// Define transformation of CDir3 as well, to return CDir3.
//
template CDir3 operator *(const CDir3
& d3, const CRotate3
& r3)
{
// Transform dir like a vector, then copy to a direction, bypassing renormalisation.
return CDir3
((CVector3
&)d3 * r3, true);
}
//**********************************************************************************************
//
template class CPlacement3
//
// Prefix: p3
//
// A rigid transform, capable of describing an object's placement in a coordinate system.
// Contains a rotation and a translation vector.
//
//**************************************
{
public:
CVector3 v3Pos; // The translation to add.
CRotate3
r3Rot; // The non-translating transform.
public:
//******************************************************************************************
//
// Constructors.
//
CPlacement3()
: v3Pos(0, 0, 0)
{
}
//
// Provide constructors for all combinations of rotation and translation.
//
CPlacement3(const CRotate3
& r3, const CVector3
& v3)
: r3Rot(r3), v3Pos(v3)
{
}
CPlacement3(const CRotate3
& r3)
: r3Rot(r3), v3Pos(0, 0, 0)
{
}
CPlacement3(const CVector3
& v3)
: v3Pos(v3)
{
}
//******************************************************************************************
//
// Member functions.
//
//******************************************************************************************
//
void SetOrigin
(
const CVector3
& v3_origin // Point acting as origin of rotation.
)
//
// Adjusts the transform so that the rotation is centred on v3_origin.
//
//**********************************
{
//
// To do the operation, first translate the object from v3_at back to the origin,
// do the rotation, then put the object back at v3_at.
//
// This is the same as just setting the translation to -v3_at * mx3 + v3_at.
//
v3Pos = -v3_origin * r3Rot + v3_origin;
}
//******************************************************************************************
//
void AdjustOrigin
(
const CVector3
& v3_origin // Point acting as origin of transformation.
)
//
// Adjusts the placement so that it is centred on v3_origin. This is similar
// to SetOrigin() above, except that the placement's current translation is kept, and added
// to the new translation.
//
//**********************************
{
v3Pos += -v3_origin * r3Rot + v3_origin;
}
//******************************************************************************************
//
// Conversion operators.
//
operator CTransform3
() const
{
return CTransform3
((CMatrix3
)r3Rot, v3Pos);
}
//******************************************************************************************
//
// Operators.
//
// Return the inverse of the transform.
CPlacement3
operator ~() const
{
// The inverse of a composite operation S*TR is ~TR * ~S, which is CPlacement3(~S, -TR * ~S).
CRotate3
r3_inverse = ~r3Rot;
return CPlacement3
(r3_inverse, -v3Pos * r3_inverse);
}
//
// Concatenate with another CPlacement3.
//
CPlacement3
operator *(const CPlacement3
& p3) const
{
return CPlacement3
(r3Rot * p3.r3Rot, v3Pos * p3);
}
CPlacement3
& operator *=(const CPlacement3
& p3)
{
// Concatenate base transform and translation separately.
r3Rot *= p3.r3Rot;
v3Pos *= p3;
return *this;
}
//
// Concatenate with simple rotation of same type.
//
CPlacement3
operator *(const CRotate3
& r3) const
{
return CPlacement3
(r3Rot * r3, v3Pos * r3);
}
CPlacement3
& operator *=(const CRotate3
& r3)
{
// Concatenate base transform and translation separately.
r3Rot *= r3;
v3Pos *= r3;
return *this;
}
//
// Concatenate with a translation.
//
CPlacement3
operator *(const CTranslate3
& tl3) const
{
return CPlacement3
(r3Rot, v3Pos + tl3.v3Pos);
}
CPlacement3
& operator *=(const CTranslate3
& tl3)
{
v3Pos += tl3.v3Pos;
return *this;
}
};
//**********************************************************************************************
//
// Global operators for CRotate3<>.
//
template inline CPlacement3 operator *(const CRotate3
& r3, const CPlacement3
& p3)
{
return CPlacement3
(r3 * p3.r3Rot, p3.v3Pos);
}
template inline CPlacement3 operator *(const CTranslate3
& tl3, const CPlacement3
& p3)
{
return CPlacement3
(p3.r3Rot, tl3.v3Pos * p3);
}
//
// Combine a rotation and translation into a placement.
//
template inline CPlacement3 operator *(const CRotate3
& r3, const CTranslate3
& tl3)
{
return CPlacement3
(r3, tl3.v3Pos);
}
template inline CPlacement3 operator *(const CTranslate3
& tl3, const CRotate3
& r3)
{
return CPlacement3
(r3, tl3.v3Pos * r3);
}
//
// Operate on a vector.
//
template inline CVector3 operator *(const CVector3
& v3, const CPlacement3
& p3)
{
// Perform the transform, then add the translation.
return v3 * p3.r3Rot + p3.v3Pos;
}
template inline CVector3& operator *=(CVector3
& v3, const CPlacement3
& p3)
{
v3 *= p3.r3Rot;
v3 += p3.v3Pos;
return v3;
}
//
// Operate on a direction by skipping the translation step.
//
template inline CDir3 operator *(const CDir3
& d3, const CPlacement3
& p3)
{
return d3 * p3.r3Rot;
}
template inline CDir3& operator *=(CDir3
& d3, const CPlacement3
& p3)
{
return d3 *= p3.r3Rot;
}
//**********************************************************************************************
//
// Global functions for CRotate3<>.
//
//******************************************************************************************
//
template inline CPlacement3 TransformAt(const CRotate3
& r3, const CVector3
& v3_at)
//
// Returns:
// A CPlacement3 which performs the rotation r3 as if the point v3_at
// were the origin.
//
// Notes:
// This function does not have a type prefix because it is meant to be generic.
// There is another version for CMatrix3 and CTransform3.
//
//**********************************
{
//
// To do the operation, first translate the object from v3_at back to the origin,
// do the rotation, then put the object back at v3_at.
//
// This is the same as just setting the translation to -v3_at * r3 + v3_at.
//
return CPlacement3
(r3, (-v3_at) * r3 + v3_at);
}
//******************************************************************************************
//
// We must include all the implementation code because this is a template class.
//
#include "Rotate.cpp"
#endif