/** 
 * @file llcoordframe.cpp
 * @brief LLCoordFrame 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 "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