/** * @file llcoordframe.cpp * @brief LLCoordFrame class implementation. * * $LicenseInfo:firstyear=2000&license=viewergpl$ * * Copyright (c) 2000-2008, 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 "vmath.h" #include "v3math.h" #include "m3math.h" #include "v4math.h" #include "m4math.h" #include "llquaternion.h" #include "llcoordframe.h" #ifndef X_AXIS #define X_AXIS 1.0f,0.0f,0.0f #define Y_AXIS 0.0f,1.0f,0.0f #define Z_AXIS 0.0f,0.0f,1.0f #endif // Constructors LLCoordFrame::LLCoordFrame() : mOrigin(0.f, 0.f, 0.f), mXAxis(X_AXIS), mYAxis(Y_AXIS), mZAxis(Z_AXIS) { } LLCoordFrame::LLCoordFrame(const LLVector3 &origin) : mOrigin(origin), mXAxis(X_AXIS), mYAxis(Y_AXIS), mZAxis(Z_AXIS) { if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLVector3 &direction) : mOrigin(origin) { lookDir(direction); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis) : mOrigin(0.f, 0.f, 0.f), mXAxis(x_axis), mYAxis(y_axis), mZAxis(z_axis) { if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis) : mOrigin(origin), mXAxis(x_axis), mYAxis(y_axis), mZAxis(z_axis) { if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLMatrix3 &rotation) : mOrigin(origin), mXAxis(rotation.mMatrix[VX]), mYAxis(rotation.mMatrix[VY]), mZAxis(rotation.mMatrix[VZ]) { if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLQuaternion &q) : mOrigin(0.f, 0.f, 0.f) { LLMatrix3 rotation_matrix(q); mXAxis.setVec(rotation_matrix.mMatrix[VX]); mYAxis.setVec(rotation_matrix.mMatrix[VY]); mZAxis.setVec(rotation_matrix.mMatrix[VZ]); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLQuaternion &q) : mOrigin(origin) { LLMatrix3 rotation_matrix(q); mXAxis.setVec(rotation_matrix.mMatrix[VX]); mYAxis.setVec(rotation_matrix.mMatrix[VY]); mZAxis.setVec(rotation_matrix.mMatrix[VZ]); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } LLCoordFrame::LLCoordFrame(const LLMatrix4 &mat) : mOrigin(mat.mMatrix[VW]), mXAxis(mat.mMatrix[VX]), mYAxis(mat.mMatrix[VY]), mZAxis(mat.mMatrix[VZ]) { if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } // The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB /* LLCoordFrame::LLCoordFrame(const F32 *origin, const F32 *rotation) : mOrigin(origin), mXAxis(rotation+3*VX), mYAxis(rotation+3*VY), mZAxis(rotation+3*VZ) { if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } */ /* LLCoordFrame::LLCoordFrame(const F32 *origin_and_rotation) : mOrigin(origin_and_rotation), mXAxis(origin_and_rotation + 3*(VX+1)), mYAxis(origin_and_rotation + 3*(VY+1)), mZAxis(origin_and_rotation + 3*(VZ+1)) { if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl; } } */ void LLCoordFrame::reset() { mOrigin.setVec(0.0f, 0.0f, 0.0f); resetAxes(); } void LLCoordFrame::resetAxes() { mXAxis.setVec(1.0f, 0.0f, 0.0f); mYAxis.setVec(0.0f, 1.0f, 0.0f); mZAxis.setVec(0.0f, 0.0f, 1.0f); } // setOrigin() member functions set mOrigin void LLCoordFrame::setOrigin(F32 x, F32 y, F32 z) { mOrigin.setVec(x, y, z); if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setOrigin()" << llendl; } } void LLCoordFrame::setOrigin(const LLVector3 &new_origin) { mOrigin = new_origin; if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setOrigin()" << llendl; } } void LLCoordFrame::setOrigin(const F32 *origin) { mOrigin.mV[VX] = *(origin + VX); mOrigin.mV[VY] = *(origin + VY); mOrigin.mV[VZ] = *(origin + VZ); if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setOrigin()" << llendl; } } void LLCoordFrame::setOrigin(const LLCoordFrame &frame) { mOrigin = frame.getOrigin(); if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setOrigin()" << llendl; } } // setAxes() member functions set the axes, and assume that // the arguments are orthogonal and normalized. void LLCoordFrame::setAxes(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis) { mXAxis = x_axis; mYAxis = y_axis; mZAxis = z_axis; if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setAxes()" << llendl; } } void LLCoordFrame::setAxes(const LLMatrix3 &rotation_matrix) { mXAxis.setVec(rotation_matrix.mMatrix[VX]); mYAxis.setVec(rotation_matrix.mMatrix[VY]); mZAxis.setVec(rotation_matrix.mMatrix[VZ]); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setAxes()" << llendl; } } void LLCoordFrame::setAxes(const LLQuaternion &q ) { LLMatrix3 rotation_matrix(q); setAxes(rotation_matrix); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setAxes()" << llendl; } } void LLCoordFrame::setAxes( const F32 *rotation_matrix ) { mXAxis.mV[VX] = *(rotation_matrix + 3*VX + VX); mXAxis.mV[VY] = *(rotation_matrix + 3*VX + VY); mXAxis.mV[VZ] = *(rotation_matrix + 3*VX + VZ); mYAxis.mV[VX] = *(rotation_matrix + 3*VY + VX); mYAxis.mV[VY] = *(rotation_matrix + 3*VY + VY); mYAxis.mV[VZ] = *(rotation_matrix + 3*VY + VZ); mZAxis.mV[VX] = *(rotation_matrix + 3*VZ + VX); mZAxis.mV[VY] = *(rotation_matrix + 3*VZ + VY); mZAxis.mV[VZ] = *(rotation_matrix + 3*VZ + VZ); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setAxes()" << llendl; } } void LLCoordFrame::setAxes(const LLCoordFrame &frame) { mXAxis = frame.getXAxis(); mYAxis = frame.getYAxis(); mZAxis = frame.getZAxis(); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::setAxes()" << llendl; } } // translate() member functions move mOrigin to a relative position void LLCoordFrame::translate(F32 x, F32 y, F32 z) { mOrigin.mV[VX] += x; mOrigin.mV[VY] += y; mOrigin.mV[VZ] += z; if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::translate()" << llendl; } } void LLCoordFrame::translate(const LLVector3 &v) { mOrigin += v; if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::translate()" << llendl; } } void LLCoordFrame::translate(const F32 *origin) { mOrigin.mV[VX] += *(origin + VX); mOrigin.mV[VY] += *(origin + VY); mOrigin.mV[VZ] += *(origin + VZ); if( !mOrigin.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::translate()" << llendl; } } // Rotate move the axes to a relative rotation void LLCoordFrame::rotate(F32 angle, F32 x, F32 y, F32 z) { LLQuaternion q(angle, LLVector3(x,y,z)); rotate(q); } void LLCoordFrame::rotate(F32 angle, const LLVector3 &rotation_axis) { LLQuaternion q(angle, rotation_axis); rotate(q); } void LLCoordFrame::rotate(const LLQuaternion &q) { LLMatrix3 rotation_matrix(q); rotate(rotation_matrix); } void LLCoordFrame::rotate(const LLMatrix3 &rotation_matrix) { mXAxis.rotVec(rotation_matrix); mYAxis.rotVec(rotation_matrix); orthonormalize(); if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::rotate()" << llendl; } } void LLCoordFrame::roll(F32 angle) { LLQuaternion q(angle, mXAxis); LLMatrix3 rotation_matrix(q); rotate(rotation_matrix); if( !mYAxis.isFinite() || !mZAxis.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::roll()" << llendl; } } void LLCoordFrame::pitch(F32 angle) { LLQuaternion q(angle, mYAxis); LLMatrix3 rotation_matrix(q); rotate(rotation_matrix); if( !mXAxis.isFinite() || !mZAxis.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::pitch()" << llendl; } } void LLCoordFrame::yaw(F32 angle) { LLQuaternion q(angle, mZAxis); LLMatrix3 rotation_matrix(q); rotate(rotation_matrix); if( !mXAxis.isFinite() || !mYAxis.isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::yaw()" << llendl; } } // get*() routines LLQuaternion LLCoordFrame::getQuaternion() const { LLQuaternion quat(mXAxis, mYAxis, mZAxis); return quat; } void LLCoordFrame::getMatrixToLocal(LLMatrix4& mat) const { mat.setFwdCol(mXAxis); mat.setLeftCol(mYAxis); mat.setUpCol(mZAxis); mat.mMatrix[3][0] = -(mOrigin * LLVector3(mat.mMatrix[0][0], mat.mMatrix[1][0], mat.mMatrix[2][0])); mat.mMatrix[3][1] = -(mOrigin * LLVector3(mat.mMatrix[0][1], mat.mMatrix[1][1], mat.mMatrix[2][1])); mat.mMatrix[3][2] = -(mOrigin * LLVector3(mat.mMatrix[0][2], mat.mMatrix[1][2], mat.mMatrix[2][2])); } void LLCoordFrame::getRotMatrixToParent(LLMatrix4& mat) const { // Note: moves into CFR mat.setFwdRow( -mYAxis ); mat.setLeftRow( mZAxis ); mat.setUpRow( -mXAxis ); } size_t LLCoordFrame::writeOrientation(char *buffer) const { memcpy(buffer, mOrigin.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ buffer += 3*sizeof(F32); memcpy(buffer, mXAxis.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ buffer += 3*sizeof(F32); memcpy(buffer, mYAxis.mV, 3*sizeof(F32));/*Flawfinder: ignore */ buffer += 3*sizeof(F32); memcpy(buffer, mZAxis.mV, 3*sizeof(F32)); /*Flawfinder: ignore */ return 12*sizeof(F32); } size_t LLCoordFrame::readOrientation(const char *buffer) { memcpy(mOrigin.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ buffer += 3*sizeof(F32); memcpy(mXAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ buffer += 3*sizeof(F32); memcpy(mYAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ buffer += 3*sizeof(F32); memcpy(mZAxis.mV, buffer, 3*sizeof(F32)); /*Flawfinder: ignore */ if( !isFinite() ) { reset(); llwarns << "Non Finite in LLCoordFrame::readOrientation()" << llendl; } return 12*sizeof(F32); } // rotation and transform vectors between reference frames LLVector3 LLCoordFrame::rotateToLocal(const LLVector3 &absolute_vector) const { LLVector3 local_vector(mXAxis * absolute_vector, mYAxis * absolute_vector, mZAxis * absolute_vector); return local_vector; } LLVector4 LLCoordFrame::rotateToLocal(const LLVector4 &absolute_vector) const { LLVector4 local_vector; local_vector.mV[VX] = mXAxis.mV[VX] * absolute_vector.mV[VX] + mXAxis.mV[VY] * absolute_vector.mV[VY] + mXAxis.mV[VZ] * absolute_vector.mV[VZ]; local_vector.mV[VY] = mYAxis.mV[VX] * absolute_vector.mV[VX] + mYAxis.mV[VY] * absolute_vector.mV[VY] + mYAxis.mV[VZ] * absolute_vector.mV[VZ]; local_vector.mV[VZ] = mZAxis.mV[VX] * absolute_vector.mV[VX] + mZAxis.mV[VY] * absolute_vector.mV[VY] + mZAxis.mV[VZ] * absolute_vector.mV[VZ]; local_vector.mV[VW] = absolute_vector.mV[VW]; return local_vector; } LLVector3 LLCoordFrame::rotateToAbsolute(const LLVector3 &local_vector) const { LLVector3 absolute_vector; absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] + mYAxis.mV[VX] * local_vector.mV[VY] + mZAxis.mV[VX] * local_vector.mV[VZ]; absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] + mYAxis.mV[VY] * local_vector.mV[VY] + mZAxis.mV[VY] * local_vector.mV[VZ]; absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] + mYAxis.mV[VZ] * local_vector.mV[VY] + mZAxis.mV[VZ] * local_vector.mV[VZ]; return absolute_vector; } LLVector4 LLCoordFrame::rotateToAbsolute(const LLVector4 &local_vector) const { LLVector4 absolute_vector; absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] + mYAxis.mV[VX] * local_vector.mV[VY] + mZAxis.mV[VX] * local_vector.mV[VZ]; absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] + mYAxis.mV[VY] * local_vector.mV[VY] + mZAxis.mV[VY] * local_vector.mV[VZ]; absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] + mYAxis.mV[VZ] * local_vector.mV[VY] + mZAxis.mV[VZ] * local_vector.mV[VZ]; absolute_vector.mV[VW] = local_vector[VW]; return absolute_vector; } void LLCoordFrame::orthonormalize() // Makes sure the axes are orthogonal and normalized. { mXAxis.normVec(); // X is renormalized mYAxis -= mXAxis * (mXAxis * mYAxis); // Y remains in X-Y plane mYAxis.normVec(); // Y is normalized mZAxis = mXAxis % mYAxis; // Z = X cross Y } LLVector3 LLCoordFrame::transformToLocal(const LLVector3 &absolute_vector) const { return rotateToLocal(absolute_vector - mOrigin); } LLVector4 LLCoordFrame::transformToLocal(const LLVector4 &absolute_vector) const { LLVector4 local_vector(absolute_vector); local_vector.mV[VX] -= mOrigin.mV[VX]; local_vector.mV[VY] -= mOrigin.mV[VY]; local_vector.mV[VZ] -= mOrigin.mV[VZ]; return rotateToLocal(local_vector); } LLVector3 LLCoordFrame::transformToAbsolute(const LLVector3 &local_vector) const { return (rotateToAbsolute(local_vector) + mOrigin); } LLVector4 LLCoordFrame::transformToAbsolute(const LLVector4 &local_vector) const { LLVector4 absolute_vector; absolute_vector = rotateToAbsolute(local_vector); absolute_vector.mV[VX] += mOrigin.mV[VX]; absolute_vector.mV[VY] += mOrigin.mV[VY]; absolute_vector.mV[VZ] += mOrigin.mV[VZ]; return absolute_vector; } // This is how you combine a translation and rotation of a // coordinate frame to get an OpenGL transformation matrix: // // translation * rotation = transformation matrix // // (i)-> // (j)| 1 0 0 0 | | a d g 0 | | a d g 0 | // | | 0 1 0 0 | * | b e h 0 | = | b e h 0 | // V | 0 0 1 0 | | c f i 0 | | c f i 0 | // |-x -y -z 1 | | 0 0 0 1 | |-(ax+by+cz) -(dx+ey+fz) -(gx+hy+iz) 1 | // // where {a,b,c} = x-axis // {d,e,f} = y-axis // {g,h,i} = z-axis // {x,y,z} = origin void LLCoordFrame::getOpenGLTranslation(F32 *ogl_matrix) const { *(ogl_matrix + 0) = 1.0f; *(ogl_matrix + 1) = 0.0f; *(ogl_matrix + 2) = 0.0f; *(ogl_matrix + 3) = 0.0f; *(ogl_matrix + 4) = 0.0f; *(ogl_matrix + 5) = 1.0f; *(ogl_matrix + 6) = 0.0f; *(ogl_matrix + 7) = 0.0f; *(ogl_matrix + 8) = 0.0f; *(ogl_matrix + 9) = 0.0f; *(ogl_matrix + 10) = 1.0f; *(ogl_matrix + 11) = 0.0f; *(ogl_matrix + 12) = -mOrigin.mV[VX]; *(ogl_matrix + 13) = -mOrigin.mV[VY]; *(ogl_matrix + 14) = -mOrigin.mV[VZ]; *(ogl_matrix + 15) = 1.0f; } void LLCoordFrame::getOpenGLRotation(F32 *ogl_matrix) const { *(ogl_matrix + 0) = mXAxis.mV[VX]; *(ogl_matrix + 4) = mXAxis.mV[VY]; *(ogl_matrix + 8) = mXAxis.mV[VZ]; *(ogl_matrix + 1) = mYAxis.mV[VX]; *(ogl_matrix + 5) = mYAxis.mV[VY]; *(ogl_matrix + 9) = mYAxis.mV[VZ]; *(ogl_matrix + 2) = mZAxis.mV[VX]; *(ogl_matrix + 6) = mZAxis.mV[VY]; *(ogl_matrix + 10) = mZAxis.mV[VZ]; *(ogl_matrix + 3) = 0.0f; *(ogl_matrix + 7) = 0.0f; *(ogl_matrix + 11) = 0.0f; *(ogl_matrix + 12) = 0.0f; *(ogl_matrix + 13) = 0.0f; *(ogl_matrix + 14) = 0.0f; *(ogl_matrix + 15) = 1.0f; } void LLCoordFrame::getOpenGLTransform(F32 *ogl_matrix) const { *(ogl_matrix + 0) = mXAxis.mV[VX]; *(ogl_matrix + 4) = mXAxis.mV[VY]; *(ogl_matrix + 8) = mXAxis.mV[VZ]; *(ogl_matrix + 12) = -mOrigin * mXAxis; *(ogl_matrix + 1) = mYAxis.mV[VX]; *(ogl_matrix + 5) = mYAxis.mV[VY]; *(ogl_matrix + 9) = mYAxis.mV[VZ]; *(ogl_matrix + 13) = -mOrigin * mYAxis; *(ogl_matrix + 2) = mZAxis.mV[VX]; *(ogl_matrix + 6) = mZAxis.mV[VY]; *(ogl_matrix + 10) = mZAxis.mV[VZ]; *(ogl_matrix + 14) = -mOrigin * mZAxis; *(ogl_matrix + 3) = 0.0f; *(ogl_matrix + 7) = 0.0f; *(ogl_matrix + 11) = 0.0f; *(ogl_matrix + 15) = 1.0f; } // at and up_direction are presumed to be normalized void LLCoordFrame::lookDir(const LLVector3 &at, const LLVector3 &up_direction) { // Make sure 'at' and 'up_direction' are not parallel // and that neither are zero-length vectors LLVector3 left(up_direction % at); if (left.isNull()) { //tweak lookat pos so we don't get a degenerate matrix LLVector3 tempat(at[VX] + 0.01f, at[VY], at[VZ]); tempat.normVec(); left = (up_direction % tempat); } left.normVec(); LLVector3 up = at % left; if (at.isFinite() && left.isFinite() && up.isFinite()) { setAxes(at, left, up); } } void LLCoordFrame::lookDir(const LLVector3 &xuv) { static LLVector3 up_direction(0.0f, 0.0f, 1.0f); lookDir(xuv, up_direction); } void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up_direction) { setOrigin(origin); LLVector3 at(point_of_interest - origin); at.normVec(); lookDir(at, up_direction); } void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest) { static LLVector3 up_direction(0.0f, 0.0f, 1.0f); setOrigin(origin); LLVector3 at(point_of_interest - origin); at.normVec(); lookDir(at, up_direction); } // Operators and friends std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C) { s << "{ " << " origin = " << C.mOrigin << " x_axis = " << C.mXAxis << " y_axis = " << C.mYAxis << " z_axis = " << C.mZAxis << " }"; return s; } // Private member functions //EOF