123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892 |
- /**
- * @file llquaternion.cpp
- * @brief LLQuaternion class implementation.
- *
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "llmath.h" // For F_PI and GIMBAL_THRESHOLD
- #include "llquaternion.h"
- #include "llquantize.h"
- #include "llmatrix3.h"
- #include "llmatrix4.h"
- #include "llvector3d.h"
- #include "llvector3.h"
- #include "llvector4.h"
- // WARNING: Do not use this for global const definitions ! Using this at the
- // top of a *.cpp file might not give you what you think.
- const LLQuaternion LLQuaternion::DEFAULT;
- // Constructors
- LLQuaternion::LLQuaternion(const LLMatrix4& mat) noexcept
- {
- *this = mat.quaternion();
- normalize();
- }
- LLQuaternion::LLQuaternion(const LLMatrix3& mat) noexcept
- {
- *this = mat.quaternion();
- normalize();
- }
- LLQuaternion::LLQuaternion(F32 angle, const LLVector4& vec) noexcept
- {
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] +
- vec.mV[VY] * vec.mV[VY] +
- vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- }
- LLQuaternion::LLQuaternion(F32 angle, const LLVector3& vec) noexcept
- {
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] +
- vec.mV[VY] * vec.mV[VY] +
- vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- }
- LLQuaternion::LLQuaternion(const LLVector3& x_axis, const LLVector3& y_axis,
- const LLVector3& z_axis) noexcept
- {
- LLMatrix3 mat;
- mat.setRows(x_axis, y_axis, z_axis);
- *this = mat.quaternion();
- normalize();
- }
- LLQuaternion::LLQuaternion(const LLSD& sd)
- {
- setValue(sd);
- }
- // Quatizations
- void LLQuaternion::quantize16(F32 lower, F32 upper)
- {
- F32 x = mQ[VX];
- F32 y = mQ[VY];
- F32 z = mQ[VZ];
- F32 s = mQ[VS];
- x = U16_to_F32(F32_to_U16_ROUND(x, lower, upper), lower, upper);
- y = U16_to_F32(F32_to_U16_ROUND(y, lower, upper), lower, upper);
- z = U16_to_F32(F32_to_U16_ROUND(z, lower, upper), lower, upper);
- s = U16_to_F32(F32_to_U16_ROUND(s, lower, upper), lower, upper);
- mQ[VX] = x;
- mQ[VY] = y;
- mQ[VZ] = z;
- mQ[VS] = s;
- normalize();
- }
- void LLQuaternion::quantize8(F32 lower, F32 upper)
- {
- mQ[VX] = U8_to_F32(F32_to_U8_ROUND(mQ[VX], lower, upper), lower, upper);
- mQ[VY] = U8_to_F32(F32_to_U8_ROUND(mQ[VY], lower, upper), lower, upper);
- mQ[VZ] = U8_to_F32(F32_to_U8_ROUND(mQ[VZ], lower, upper), lower, upper);
- mQ[VS] = U8_to_F32(F32_to_U8_ROUND(mQ[VS], lower, upper), lower, upper);
- normalize();
- }
- // LLVector3 Magnitude and Normalization Functions
- // Set LLQuaternion routines
- const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, F32 x, F32 y, F32 z)
- {
- F32 mag = sqrtf(x * x + y * y + z * z);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = x * s;
- mQ[VY] = y * s;
- mQ[VZ] = z * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return *this;
- }
- const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, const LLVector3& vec)
- {
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] +
- vec.mV[VY] * vec.mV[VY] +
- vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return *this;
- }
- const LLQuaternion& LLQuaternion::setAngleAxis(F32 angle, const LLVector4& vec)
- {
- F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] +
- vec.mV[VY] * vec.mV[VY] +
- vec.mV[VZ] * vec.mV[VZ]);
- if (mag > FP_MAG_THRESHOLD)
- {
- angle *= 0.5;
- F32 c = cosf(angle);
- F32 s = sinf(angle) / mag;
- mQ[VX] = vec.mV[VX] * s;
- mQ[VY] = vec.mV[VY] * s;
- mQ[VZ] = vec.mV[VZ] * s;
- mQ[VW] = c;
- }
- else
- {
- loadIdentity();
- }
- return *this;
- }
- const LLQuaternion& LLQuaternion::setEulerAngles(F32 roll, F32 pitch, F32 yaw)
- {
- LLMatrix3 rot_mat(roll, pitch, yaw);
- rot_mat.orthogonalize();
- *this = rot_mat.quaternion();
- normalize();
- return *this;
- }
- const LLQuaternion& LLQuaternion::set(const LLMatrix3& mat)
- {
- *this = mat.quaternion();
- normalize();
- return *this;
- }
- const LLQuaternion& LLQuaternion::set(const LLMatrix4& mat)
- {
- *this = mat.quaternion();
- normalize();
- return *this;
- }
- // SJB: This code is correct for a logically stored (non-transposed) matrix;
- // Our matrices are stored transposed, OpenGL style, so this generates the
- // INVERSE matrix, or the CORRECT matrix form an INVERSE quaternion.
- // Because we use similar logic in LLMatrix3::quaternion(), we are internally
- // consistant so everything works OK :)
- LLMatrix3 LLQuaternion::getMatrix3() const
- {
- F32 xx = mQ[VX] * mQ[VX];
- F32 xy = mQ[VX] * mQ[VY];
- F32 xz = mQ[VX] * mQ[VZ];
- F32 xw = mQ[VX] * mQ[VW];
- F32 yy = mQ[VY] * mQ[VY];
- F32 yz = mQ[VY] * mQ[VZ];
- F32 yw = mQ[VY] * mQ[VW];
- F32 zz = mQ[VZ] * mQ[VZ];
- F32 zw = mQ[VZ] * mQ[VW];
- LLMatrix3 mat;
- mat.mMatrix[0][0] = 1.f - 2.f * (yy + zz);
- mat.mMatrix[0][1] = 2.f * (xy + zw);
- mat.mMatrix[0][2] = 2.f * (xz - yw);
- mat.mMatrix[1][0] = 2.f * (xy - zw);
- mat.mMatrix[1][1] = 1.f - 2.f * (xx + zz);
- mat.mMatrix[1][2] = 2.f * (yz + xw);
- mat.mMatrix[2][0] = 2.f * (xz + yw);
- mat.mMatrix[2][1] = 2.f * (yz - xw);
- mat.mMatrix[2][2] = 1.f - 2.f * (xx + yy);
- return mat;
- }
- LLMatrix4 LLQuaternion::getMatrix4() const
- {
- F32 xx = mQ[VX] * mQ[VX];
- F32 xy = mQ[VX] * mQ[VY];
- F32 xz = mQ[VX] * mQ[VZ];
- F32 xw = mQ[VX] * mQ[VW];
- F32 yy = mQ[VY] * mQ[VY];
- F32 yz = mQ[VY] * mQ[VZ];
- F32 yw = mQ[VY] * mQ[VW];
- F32 zz = mQ[VZ] * mQ[VZ];
- F32 zw = mQ[VZ] * mQ[VW];
- LLMatrix4 mat;
- mat.mMatrix[0][0] = 1.f - 2.f * (yy + zz);
- mat.mMatrix[0][1] = 2.f * (xy + zw);
- mat.mMatrix[0][2] = 2.f * (xz - yw);
- mat.mMatrix[1][0] = 2.f * (xy - zw);
- mat.mMatrix[1][1] = 1.f - 2.f * (xx + zz);
- mat.mMatrix[1][2] = 2.f * (yz + xw);
- mat.mMatrix[2][0] = 2.f * (xz + yw);
- mat.mMatrix[2][1] = 2.f * (yz - xw);
- mat.mMatrix[2][2] = 1.f - 2.f * (xx + yy);
- // *TODO: should we set the translation portion to zero ?
- return mat;
- }
- // Other useful methods
- // calculate the shortest rotation from a to b
- void LLQuaternion::shortestArc(const LLVector3& a, const LLVector3& b)
- {
- F32 ab = a * b; // dotproduct
- LLVector3 c = a % b; // crossproduct
- F32 cc = c * c; // squared length of the crossproduct
- if (ab * ab + cc) // test if the arguments have sufficient magnitude
- {
- // Test if the arguments are (anti)parallel
- if (cc > 0.f)
- {
- // Note: do not try to optimize this line
- F32 s = sqrtf(ab * ab + cc) + ab;
- // The inverted magnitude of the quaternion
- F32 m = 1.f / sqrtf(cc + s * s);
- mQ[VX] = c.mV[VX] * m;
- mQ[VY] = c.mV[VY] * m;
- mQ[VZ] = c.mV[VZ] * m;
- mQ[VW] = s * m;
- return;
- }
- // Test if the angle is bigger than PI/2 (anti parallel)
- if (ab < 0.f)
- {
- // The arguments are anti-parallel, we have to choose an axis
- c = a - b;
- // The length projected on the XY-plane
- F32 m = sqrtf(c.mV[VX] * c.mV[VX] + c.mV[VY] * c.mV[VY]);
- if (m > FP_MAG_THRESHOLD)
- {
- // Return the quaternion with the axis in the XY-plane
- mQ[VX] = -c.mV[VY] / m;
- mQ[VY] = c.mV[VX] / m;
- mQ[VZ] = mQ[VW] = 0.f;
- return;
- }
- else // the vectors are parallel to the Z-axis
- {
- mQ[VX] = 1.f; // rotate around the X-axis
- mQ[VY] = mQ[VZ] = mQ[VW] = 0.f;
- return;
- }
- }
- }
- loadIdentity();
- }
- // constrains rotation to a cone angle specified in radians
- const LLQuaternion& LLQuaternion::constrain(F32 radians)
- {
- const F32 cos_angle_lim = cosf(radians / 2); // mQ[VW] limit
- const F32 sin_angle_lim = sinf(radians / 2); // rotation axis length limit
- if (mQ[VW] < 0.f)
- {
- mQ[VX] *= -1.f;
- mQ[VY] *= -1.f;
- mQ[VZ] *= -1.f;
- mQ[VW] *= -1.f;
- }
- // if rotation angle is greater than limit (cos is less than limit)
- if (mQ[VW] < cos_angle_lim)
- {
- mQ[VW] = cos_angle_lim;
- F32 axis_len = sqrtf(mQ[VX] * mQ[VX] + mQ[VY] * mQ[VY] +
- mQ[VZ] * mQ[VZ]); // sinf(theta/2)
- F32 axis_mult_fact = sin_angle_lim / axis_len;
- mQ[VX] *= axis_mult_fact;
- mQ[VY] *= axis_mult_fact;
- mQ[VZ] *= axis_mult_fact;
- }
- return *this;
- }
- // Operators
- std::ostream& operator<<(std::ostream& s, const LLQuaternion& a)
- {
- s << "{ "
- << a.mQ[VX] << ", " << a.mQ[VY] << ", " << a.mQ[VZ] << ", " << a.mQ[VW]
- << " }";
- return s;
- }
- // Does NOT renormalize the result
- LLQuaternion operator*(const LLQuaternion& a, const LLQuaternion& b)
- {
- LLQuaternion q(b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] +
- b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1],
- b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] +
- b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2],
- b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] +
- b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0],
- b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] -
- b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2]);
- return q;
- }
- #if 0
- LLMatrix4 operator*(const LLMatrix4& m, const LLQuaternion& q)
- {
- LLMatrix4 qmat(q);
- return (m*qmat);
- }
- #endif
- LLVector4 operator*(const LLVector4& a, const LLQuaternion& rot)
- {
- F32 rw = -rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] -
- rot.mQ[VZ] * a.mV[VZ];
- F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] -
- rot.mQ[VZ] * a.mV[VY];
- F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] -
- rot.mQ[VX] * a.mV[VZ];
- F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] -
- rot.mQ[VY] * a.mV[VX];
- F32 nx = -rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] +
- rz * rot.mQ[VY];
- F32 ny = -rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] +
- rx * rot.mQ[VZ];
- F32 nz = -rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] +
- ry * rot.mQ[VX];
- return LLVector4(nx, ny, nz, a.mV[VW]);
- }
- LLVector3 operator*(const LLVector3& a, const LLQuaternion& rot)
- {
- F32 rw = -rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] -
- rot.mQ[VZ] * a.mV[VZ];
- F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] -
- rot.mQ[VZ] * a.mV[VY];
- F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] -
- rot.mQ[VX] * a.mV[VZ];
- F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] -
- rot.mQ[VY] * a.mV[VX];
- F32 nx = -rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] +
- rz * rot.mQ[VY];
- F32 ny = -rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] +
- rx * rot.mQ[VZ];
- F32 nz = -rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] +
- ry * rot.mQ[VX];
- return LLVector3(nx, ny, nz);
- }
- LLVector3d operator*(const LLVector3d& a, const LLQuaternion& rot)
- {
- F64 rw = -rot.mQ[VX] * a.mdV[VX] - rot.mQ[VY] * a.mdV[VY] -
- rot.mQ[VZ] * a.mdV[VZ];
- F64 rx = rot.mQ[VW] * a.mdV[VX] + rot.mQ[VY] * a.mdV[VZ] -
- rot.mQ[VZ] * a.mdV[VY];
- F64 ry = rot.mQ[VW] * a.mdV[VY] + rot.mQ[VZ] * a.mdV[VX] -
- rot.mQ[VX] * a.mdV[VZ];
- F64 rz = rot.mQ[VW] * a.mdV[VZ] + rot.mQ[VX] * a.mdV[VY] -
- rot.mQ[VY] * a.mdV[VX];
- F64 nx = -rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] +
- rz * rot.mQ[VY];
- F64 ny = -rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] +
- rx * rot.mQ[VZ];
- F64 nz = -rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] +
- ry * rot.mQ[VX];
- return LLVector3d(nx, ny, nz);
- }
- F32 dot(const LLQuaternion& a, const LLQuaternion& b)
- {
- return a.mQ[VX] * b.mQ[VX] + a.mQ[VY] * b.mQ[VY] + a.mQ[VZ] * b.mQ[VZ] +
- a.mQ[VW] * b.mQ[VW];
- }
- #if 0 // DEMO HACK: This lerp is probably incorrect now due intermediate
- // normalization it should look more like the lerp below.
- // linear interpolation
- LLQuaternion lerp(F32 t, const LLQuaternion& p, const LLQuaternion& q)
- {
- LLQuaternion r;
- r = t * (q - p) + p;
- r.normalize();
- return r;
- }
- #endif
- // lerp from identity to q
- LLQuaternion lerp(F32 t, const LLQuaternion& q)
- {
- LLQuaternion r;
- r.mQ[VX] = t * q.mQ[VX];
- r.mQ[VY] = t * q.mQ[VY];
- r.mQ[VZ] = t * q.mQ[VZ];
- r.mQ[VW] = t * (q.mQ[VZ] - 1.f) + 1.f;
- r.normalize();
- return r;
- }
- LLQuaternion lerp(F32 t, const LLQuaternion& p, const LLQuaternion& q)
- {
- F32 inv_t = 1.f - t;
- LLQuaternion r;
- r.mQ[VX] = t * q.mQ[VX] + inv_t * p.mQ[VX];
- r.mQ[VY] = t * q.mQ[VY] + inv_t * p.mQ[VY];
- r.mQ[VZ] = t * q.mQ[VZ] + inv_t * p.mQ[VZ];
- r.mQ[VW] = t * q.mQ[VW] + inv_t * p.mQ[VW];
- r.normalize();
- return r;
- }
- // spherical linear interpolation
- LLQuaternion slerp(F32 u, const LLQuaternion& a, const LLQuaternion& b)
- {
- // cosine theta = dot product of a and b
- F32 cos_t = a.mQ[0] * b.mQ[0] + a.mQ[1] * b.mQ[1] +
- a.mQ[2] * b.mQ[2] + a.mQ[3] * b.mQ[3];
- // if b is on opposite hemisphere from a, use -a instead
- bool bflip = false;
- if (cos_t < 0.0f)
- {
- cos_t = -cos_t;
- bflip = true;
- }
- // If B is (within precision limits) the same as A, just linear interpolate
- // between A and B.
- F32 alpha; // interpolant
- F32 beta; // 1 - interpolant
- if (1.f - cos_t < 0.00001f)
- {
- beta = 1.f - u;
- alpha = u;
- }
- else
- {
- F32 theta = acosf(cos_t);
- F32 sin_t = sinf(theta);
- beta = sinf(theta - u * theta) / sin_t;
- alpha = sinf(u * theta) / sin_t;
- }
- if (bflip)
- {
- beta = -beta;
- }
- // interpolate
- LLQuaternion ret;
- ret.mQ[0] = beta * a.mQ[0] + alpha * b.mQ[0];
- ret.mQ[1] = beta * a.mQ[1] + alpha * b.mQ[1];
- ret.mQ[2] = beta * a.mQ[2] + alpha * b.mQ[2];
- ret.mQ[3] = beta * a.mQ[3] + alpha * b.mQ[3];
- return ret;
- }
- // lerp whenever possible
- LLQuaternion nlerp(F32 t, const LLQuaternion& a, const LLQuaternion& b)
- {
- if (dot(a, b) < 0.f)
- {
- return slerp(t, a, b);
- }
- else
- {
- return lerp(t, a, b);
- }
- }
- LLQuaternion nlerp(F32 t, const LLQuaternion& q)
- {
- if (q.mQ[VW] < 0.f)
- {
- return slerp(t, q);
- }
- else
- {
- return lerp(t, q);
- }
- }
- // slerp from identity quaternion to another quaternion
- LLQuaternion slerp(F32 t, const LLQuaternion& q)
- {
- F32 c = q.mQ[VW];
- if (t == 1.f || c == 1.f)
- {
- // the trivial cases
- return q;
- }
- LLQuaternion r;
- F32 angle, stq, stp;
- F32 s = sqrtf(1.f - c * c);
- if (c < 0.f)
- {
- // when c < 0.0 then theta > PI/2, since quat and -quat are the same
- // rotation we invert one of p or q to reduce unecessary spins. An
- // equivalent way to do it is to convert acosf(c) as if it had been
- // negative, and to negate stp
- F32 angle = acosf(-c);
- stp = -sinf(angle * (1.f - t));
- stq = sinf(angle * t);
- }
- else
- {
- angle = acosf(c);
- stp = sinf(angle * (1.f - t));
- stq = sinf(angle * t);
- }
- r.mQ[VX] = (q.mQ[VX] * stq) / s;
- r.mQ[VY] = (q.mQ[VY] * stq) / s;
- r.mQ[VZ] = (q.mQ[VZ] * stq) / s;
- r.mQ[VW] = (stp + q.mQ[VW] * stq) / s;
- return r;
- }
- LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order)
- {
- LLQuaternion xQ(xRot * DEG_TO_RAD, LLVector3(1.f, 0.f, 0.f));
- LLQuaternion yQ(yRot * DEG_TO_RAD, LLVector3(0.f, 1.f, 0.f));
- LLQuaternion zQ(zRot * DEG_TO_RAD, LLVector3(0.f, 0.f, 1.f));
- LLQuaternion ret;
- switch (order)
- {
- case LLQuaternion::XYZ:
- ret = xQ * yQ * zQ;
- break;
- case LLQuaternion::YZX:
- ret = yQ * zQ * xQ;
- break;
- case LLQuaternion::ZXY:
- ret = zQ * xQ * yQ;
- break;
- case LLQuaternion::XZY:
- ret = xQ * zQ * yQ;
- break;
- case LLQuaternion::YXZ:
- ret = yQ * xQ * zQ;
- break;
- case LLQuaternion::ZYX:
- ret = zQ * yQ * xQ;
- }
- return ret;
- }
- const char* OrderToString(const LLQuaternion::Order order)
- {
- switch (order)
- {
- default:
- case LLQuaternion::XYZ:
- return "XYZ";
- case LLQuaternion::YZX:
- return "YZX";
- case LLQuaternion::ZXY:
- return "ZXY";
- case LLQuaternion::XZY:
- return "XZY";
- case LLQuaternion::YXZ:
- return "YXZ";
- case LLQuaternion::ZYX:
- return "ZYX";
- }
- }
- LLQuaternion::Order StringToOrder(const char* str)
- {
- if (strncmp(str, "XYZ", 3) == 0 || strncmp(str, "xyz", 3) == 0)
- return LLQuaternion::XYZ;
- if (strncmp(str, "YZX", 3) == 0 || strncmp(str, "yzx", 3) == 0)
- return LLQuaternion::YZX;
- if (strncmp(str, "ZXY", 3) == 0 || strncmp(str, "zxy", 3) == 0)
- return LLQuaternion::ZXY;
- if (strncmp(str, "XZY", 3) == 0 || strncmp(str, "xzy", 3) == 0)
- return LLQuaternion::XZY;
- if (strncmp(str, "YXZ", 3) == 0 || strncmp(str, "yxz", 3) == 0)
- return LLQuaternion::YXZ;
- if (strncmp(str, "ZYX", 3) == 0 || strncmp(str, "zyx", 3) == 0)
- return LLQuaternion::ZYX;
- return LLQuaternion::XYZ;
- }
- void LLQuaternion::getAngleAxis(F32* angle, LLVector3& vec) const
- {
- // length of the vector-component
- F32 v = sqrtf(mQ[VX] * mQ[VX] + mQ[VY] * mQ[VY] + mQ[VZ] * mQ[VZ]);
- if (v > FP_MAG_THRESHOLD)
- {
- F32 oomag = 1.0f / v;
- F32 w = mQ[VW];
- if (mQ[VW] < 0.0f)
- {
- w = -w; // make VW positive
- oomag = -oomag; // invert the axis
- }
- vec.mV[VX] = mQ[VX] * oomag; // normalize the axis
- vec.mV[VY] = mQ[VY] * oomag;
- vec.mV[VZ] = mQ[VZ] * oomag;
- *angle = 2.0f * atan2f(v, w); // get the angle
- }
- else
- {
- *angle = 0.0f; // no rotation
- vec.mV[VX] = 0.0f; // around some dummy axis
- vec.mV[VY] = 0.0f;
- vec.mV[VZ] = 1.0f;
- }
- }
- const LLQuaternion& LLQuaternion::setFromAzimuthAndAltitude(F32 azimuth,
- F32 altitude)
- {
- // Euler angle inputs are complements of azimuth/altitude which are
- // measured from zenith
- F32 pitch = llclamp(F_PI_BY_TWO - altitude, 0.f, F_PI_BY_TWO);
- F32 yaw = llclamp(F_PI_BY_TWO - azimuth, 0.f, F_PI_BY_TWO);
- setEulerAngles(0.f, pitch, yaw);
- return *this;
- }
- void LLQuaternion::getAzimuthAndAltitude(F32& azimuth, F32& altitude)
- {
- F32 roll, pitch, yaw;
- getEulerAngles(&roll, &pitch, &yaw);
- // Make these measured from zenith
- altitude = llclamp(F_PI_BY_TWO - pitch, 0.f, F_PI_BY_TWO);
- azimuth = llclamp(F_PI_BY_TWO - yaw, 0.f, F_PI_BY_TWO);
- }
- void LLQuaternion::getAzimuthAndElevation(F32& azimuth, F32& elevation)
- {
- const LLVector3 vector_zero(1.f, 0.f, 0.f);
- LLVector3 point = vector_zero * (*this);
- if (!is_approx_zero(point.mV[VX]) || !is_approx_zero(point.mV[VY]))
- {
- azimuth = atan2f(point.mV[VX], point.mV[VY]) - F_PI_BY_TWO;
- if (azimuth < 0.f)
- {
- azimuth += F_TWO_PI;
- }
- }
- else
- {
- azimuth = F_TWO_PI - F_PI_BY_TWO;
- }
- // While vector is normalized, F32 is not sufficiently precise and we can
- // get values like 1.0000012 which will result in -PI/2 angle instead of
- // PI/2...
- elevation = asinf(llclamp(point.mV[VZ], -1.f, 1.f));
- }
- // Quaternion does not need to be normalized
- void LLQuaternion::getEulerAngles(F32* roll, F32* pitch, F32* yaw) const
- {
- F32 sx = 2 * (mQ[VX] * mQ[VW] - mQ[VY] * mQ[VZ]); // sine of the roll
- F32 sy = 2 * (mQ[VY] * mQ[VW] + mQ[VX] * mQ[VZ]); // sine of the pitch
- F32 ys = mQ[VW] * mQ[VW] - mQ[VY] * mQ[VY]; // intermediate cosine 1
- F32 xz = mQ[VX] * mQ[VX] - mQ[VZ] * mQ[VZ]; // intermediate cosine 2
- F32 cx = ys - xz; // cosine of the roll
- F32 cy = sqrtf(sx * sx + cx * cx); // cosine of the pitch
- if (cy > GIMBAL_THRESHOLD) // no gimbal lock
- {
- *roll = atan2f(sx, cx);
- *pitch = atan2f(sy, cy);
- *yaw = atan2f(2 * (mQ[VZ] * mQ[VW] - mQ[VX] * mQ[VY]), ys + xz);
- }
- else // gimbal lock
- {
- if (sy > 0)
- {
- *pitch = F_PI_BY_TWO;
- *yaw = 2 * atan2f(mQ[VZ] + mQ[VX], mQ[VW] + mQ[VY]);
- }
- else
- {
- *pitch = -F_PI_BY_TWO;
- *yaw = 2 * atan2f(mQ[VZ] - mQ[VX], mQ[VW] - mQ[VY]);
- }
- *roll = 0;
- }
- }
- // Saves space by using the fact that our quaternions are normalized
- LLVector3 LLQuaternion::packToVector3() const
- {
- F32 x = mQ[VX];
- F32 y = mQ[VY];
- F32 z = mQ[VZ];
- F32 w = mQ[VW];
- F32 mag = sqrtf(x * x + y * y + z * z + w * w);
- if (mag > FP_MAG_THRESHOLD)
- {
- x /= mag;
- y /= mag;
- z /= mag;
- // no need to normalize w since it is not used
- }
- if (mQ[VW] >= 0)
- {
- return LLVector3(x, y , z);
- }
- else
- {
- return LLVector3(-x, -y, -z);
- }
- }
- // Saves space by using the fact that our quaternions are normalized
- void LLQuaternion::unpackFromVector3(const LLVector3& vec)
- {
- mQ[VX] = vec.mV[VX];
- mQ[VY] = vec.mV[VY];
- mQ[VZ] = vec.mV[VZ];
- F32 t = 1.f - vec.lengthSquared();
- if (t > 0)
- {
- mQ[VW] = sqrtf(t);
- }
- else
- {
- // Need this to avoid trying to find the square root of a negative
- // number due to floating point error.
- mQ[VW] = 0;
- }
- }
- bool LLQuaternion::parseQuat(const std::string& buf, LLQuaternion* value)
- {
- if (buf.empty() || value == NULL)
- {
- return false;
- }
- LLQuaternion quat;
- S32 count = sscanf(buf.c_str(), "%f %f %f %f", quat.mQ + 0, quat.mQ + 1,
- quat.mQ + 2, quat.mQ + 3);
- if (4 == count)
- {
- value->set(quat);
- return true;
- }
- return false;
- }
- //static
- bool LLQuaternion::almost_equal(const LLQuaternion& a, const LLQuaternion& b,
- F32 tol_angle)
- {
- // Use the small angle approximation of cos():
- // cos(angle) ~= 1.0 - 0.5 * angle^2
- return 8.f * fabsf(1.f - fabsf(dot(a, b))) < tol_angle * tol_angle;
- }
|