/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : WWMath                                                       *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tools/max2w3d/WWmatrix3.cpp                  $*
 *                                                                                             *
 *                       Author:: Greg_h                                                       *
 *                                                                                             *
 *                     $Modtime:: 2/02/00 2:05p                                               $*
 *                                                                                             *
 *                    $Revision:: 17                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "wwmatrix3.h"
#include "matrix3d.h"
#include "matrix4.h"
#include "w3dquat.h"


/*
** Some pre-initialized Matrix3's
*/
const Matrix3 Matrix3::Identity
(
	1.0,	0.0,	0.0,
	0.0,	1.0,	0.0,
	0.0,	0.0,	1.0
);

const Matrix3 Matrix3::RotateX90
(
	1.0,	0.0,	0.0,
	0.0,	0.0, -1.0,
	0.0,	1.0,	0.0
);

const Matrix3 Matrix3::RotateX180
(
	1.0,	0.0,	0.0,
	0.0, -1.0,	0.0,
	0.0,	0.0, -1.0
);

const Matrix3 Matrix3::RotateX270
(
	1.0,	0.0,	0.0,
	0.0,	0.0,	1.0,
	0.0, -1.0,	0.0
);

const Matrix3 Matrix3::RotateY90
(
	0.0,	0.0,	1.0,
	0.0,	1.0,	0.0,
  -1.0,	0.0,	0.0
);

const Matrix3 Matrix3::RotateY180
(
  -1.0,	0.0,	0.0,
	0.0,	1.0,	0.0,
	0.0,	0.0, -1.0
);

const Matrix3 Matrix3::RotateY270
(
	0.0,	0.0, -1.0,
	0.0,	1.0,	0.0,
	1.0,	0.0,	0.0
);

const Matrix3 Matrix3::RotateZ90
(
	0.0, -1.0,	0.0,
	1.0,	0.0,	0.0,
	0.0,	0.0,	1.0
);

const Matrix3 Matrix3::RotateZ180
(
  -1.0,	0.0,	0.0,
	0.0, -1.0,	0.0,
	0.0,	0.0,	1.0
);

const Matrix3 Matrix3::RotateZ270
(
	0.0,	1.0,	0.0,
  -1.0,	0.0,	0.0,
	0.0,	0.0,	1.0
);



/*********************************************************************************************** 
 * Matrix3::Matrix3 -- Convert a Matrix3D (fake 4x4) to a Matrix3                              * 
 *                                                                                             * 
 * INPUT:                                                                                      * 
 *                                                                                             * 
 * OUTPUT:                                                                                     * 
 *                                                                                             * 
 * WARNINGS:                                                                                   * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   06/02/1997 GH  : Created.                                                                 * 
 *=============================================================================================*/
Matrix3::Matrix3(const Matrix3D & m)
{
	Row[0].Set(m[0][0],m[0][1],m[0][2]);
	Row[1].Set(m[1][0],m[1][1],m[1][2]);
	Row[2].Set(m[2][0],m[2][1],m[2][2]);
}

Matrix3::Matrix3(const Matrix4 & m)
{
	Row[0].Set(m[0][0],m[0][1],m[0][2]);
	Row[1].Set(m[1][0],m[1][1],m[1][2]);
	Row[2].Set(m[2][0],m[2][1],m[2][2]);
}

void Matrix3::Set(const Matrix3D & m)
{
	Row[0].Set(m[0][0],m[0][1],m[0][2]);
	Row[1].Set(m[1][0],m[1][1],m[1][2]);
	Row[2].Set(m[2][0],m[2][1],m[2][2]);
}

void Matrix3::Set(const Matrix4 & m)
{
	Row[0].Set(m[0][0],m[0][1],m[0][2]);
	Row[1].Set(m[1][0],m[1][1],m[1][2]);
	Row[2].Set(m[2][0],m[2][1],m[2][2]);
}

void Matrix3::Set(const Quaternion & q)
{
	Row[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
	Row[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
	Row[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));

	Row[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
	Row[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
	Row[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));

	Row[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
	Row[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
	Row[2][2] =(float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
}


Matrix3 & Matrix3::operator = (const Matrix3D & m)
{
	Row[0].Set(m[0][0],m[0][1],m[0][2]);
	Row[1].Set(m[1][0],m[1][1],m[1][2]);
	Row[2].Set(m[2][0],m[2][1],m[2][2]);
	return *this; 
}

Matrix3 & Matrix3::operator = (const Matrix4 & m)
{
	Row[0].Set(m[0][0],m[0][1],m[0][2]);
	Row[1].Set(m[1][0],m[1][1],m[1][2]);
	Row[2].Set(m[2][0],m[2][1],m[2][2]);
	return *this; 
}

void Matrix3::Multiply(const Matrix3D & a, const Matrix3 & b,Matrix3 * res)
{
	#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
    
	(*res)[0][0] = ROWCOL(0,0);
	(*res)[0][1] = ROWCOL(0,1);
	(*res)[0][2] = ROWCOL(0,2);

	(*res)[1][0] = ROWCOL(1,0);
	(*res)[1][1] = ROWCOL(1,1);
	(*res)[1][2] = ROWCOL(1,2);

	(*res)[2][0] = ROWCOL(2,0);
	(*res)[2][1] = ROWCOL(2,1);
	(*res)[2][2] = ROWCOL(2,2);

	#undef ROWCOL
}

void Matrix3::Multiply(const Matrix3 & a, const Matrix3D & b,Matrix3 * res)
{
	#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
    
	(*res)[0][0] = ROWCOL(0,0);
	(*res)[0][1] = ROWCOL(0,1);
	(*res)[0][2] = ROWCOL(0,2);

	(*res)[1][0] = ROWCOL(1,0);
	(*res)[1][1] = ROWCOL(1,1);
	(*res)[1][2] = ROWCOL(1,2);

	(*res)[2][0] = ROWCOL(2,0);
	(*res)[2][1] = ROWCOL(2,1);
	(*res)[2][2] = ROWCOL(2,2);

	#undef ROWCOL
}

Matrix3 operator * (const Matrix3D & a, const Matrix3 & b)
{
	#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
    
	return Matrix3(
			Vector3(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2) ),
			Vector3(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2) ),
			Vector3(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2) )
	);
	
	#undef ROWCOL
}

Matrix3 operator * (const Matrix3 & a, const Matrix3D & b)
{
	#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
    
	return Matrix3(
			Vector3(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2) ),
			Vector3(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2) ),
			Vector3(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2) )
	);
	
	#undef ROWCOL
}


#if 0

void Matrix3::Compute_Jacobi_Rotation(int i,int j,Matrix3 * r,Matrix3 * rinv)
{

}

void Matrix3::Symmetric_Eigen_Solve(void)
{
	Matrix3 eigen_vals = *this;
	Matrix3 eigen_vecs(1);

	Matrix3 jr,jrinv;

	while (!done) {
		eigen_vals.Compute_Jacobi_Rotation(i,j,&jr,&jrinv);
		eigen_vals = jr * (eigenvals) * jrinv;
		eigen_vecs = eigen_vecs * jr;
	}

	/*
	** Done!  Eigen values are the diagonals of
	** the eigen_vals matrix and the eigen vectors
	** are the columns of the eigen_vecs matrix
	*/

}

#endif


void Matrix3::Multiply(const Matrix3 & A,const Matrix3 & B,Matrix3 * set_res)
{
	Matrix3 tmp;
	Matrix3 * Aptr;
	float tmp1,tmp2,tmp3;

	// Check for aliased parameters, copy the 'A' matrix into a temporary if the 
	// result is going into 'A'. (in this case, this function is no better than 
	// the overloaded C++ operator...)
	if (set_res == &A) {
		tmp = A;
		Aptr = &tmp;
	} else {
		Aptr = (Matrix3 *)&A;	
	}

	tmp1 = B[0][0];
	tmp2 = B[1][0];
	tmp3 = B[2][0];

	(*set_res)[0][0] = (float)((*Aptr)[0][0]*tmp1 + (*Aptr)[0][1]*tmp2 + (*Aptr)[0][2]*tmp3);
	(*set_res)[1][0] = (float)((*Aptr)[1][0]*tmp1 + (*Aptr)[1][1]*tmp2 + (*Aptr)[1][2]*tmp3);
	(*set_res)[2][0] = (float)((*Aptr)[2][0]*tmp1 + (*Aptr)[2][1]*tmp2 + (*Aptr)[2][2]*tmp3);

	tmp1 = B[0][1];
	tmp2 = B[1][1];
	tmp3 = B[2][1];

	(*set_res)[0][1] = (float)((*Aptr)[0][0]*tmp1 + (*Aptr)[0][1]*tmp2 + (*Aptr)[0][2]*tmp3);
	(*set_res)[1][1] = (float)((*Aptr)[1][0]*tmp1 + (*Aptr)[1][1]*tmp2 + (*Aptr)[1][2]*tmp3);
	(*set_res)[2][1] = (float)((*Aptr)[2][0]*tmp1 + (*Aptr)[2][1]*tmp2 + (*Aptr)[2][2]*tmp3);

	tmp1 = B[0][2];
	tmp2 = B[1][2];
	tmp3 = B[2][2];

	(*set_res)[0][2] = (float)((*Aptr)[0][0]*tmp1 + (*Aptr)[0][1]*tmp2 + (*Aptr)[0][2]*tmp3);
	(*set_res)[1][2] = (float)((*Aptr)[1][0]*tmp1 + (*Aptr)[1][1]*tmp2 + (*Aptr)[1][2]*tmp3);
	(*set_res)[2][2] = (float)((*Aptr)[2][0]*tmp1 + (*Aptr)[2][1]*tmp2 + (*Aptr)[2][2]*tmp3);
}

int Matrix3::Is_Orthogonal(void) const
{
	Vector3 x(Row[0].X,Row[0].Y,Row[0].Z);
	Vector3 y(Row[1].X,Row[1].Y,Row[1].Z);
	Vector3 z(Row[2].X,Row[2].Y,Row[2].Z);
	
	if (Vector3::Dot_Product(x,y) > WWMATH_EPSILON) return 0;
	if (Vector3::Dot_Product(y,z) > WWMATH_EPSILON) return 0;
	if (Vector3::Dot_Product(z,x) > WWMATH_EPSILON) return 0;

	if (WWMath::Fabs(x.Length() - 1.0f) > WWMATH_EPSILON) return 0;
	if (WWMath::Fabs(y.Length() - 1.0f) > WWMATH_EPSILON) return 0;
	if (WWMath::Fabs(z.Length() - 1.0f) > WWMATH_EPSILON) return 0;

	return 1;
}

void Matrix3::Re_Orthogonalize(void)
{
	Vector3 x(Row[0][0],Row[0][1],Row[0][2]);
	Vector3 y(Row[1][0],Row[1][1],Row[1][2]);
	Vector3 z;

	Vector3::Cross_Product(x,y,&z);
	Vector3::Cross_Product(z,x,&y);

	float len = x.Length();
	if (len < WWMATH_EPSILON) {
		Make_Identity();
		return;
	} else {
		x /= len;
	}

	len = y.Length();
	if (len < WWMATH_EPSILON) {
		Make_Identity();
		return;
	} else {
		y /= len;
	}

	len = z.Length();
	if (len < WWMATH_EPSILON) {
		Make_Identity();
		return;
	} else {
		z /= len;
	}

	Row[0][0] = x.X;
	Row[0][1] = x.Y;
	Row[0][2] = x.Z;

	Row[1][0] = y.X;
	Row[1][1] = y.Y;
	Row[1][2] = y.Z;
	
	Row[2][0] = z.X;
	Row[2][1] = z.Y;
	Row[2][2] = z.Z;
}

void Matrix3::Rotate_AABox_Extent(const Vector3 & extent,Vector3 * set_extent)
{
	// push each extent out to the projections of the original extents
	for (int i=0; i<3; i++) {

		// start the center out at the translation portion of the matrix
		// and the extent at zero
		(*set_extent)[i] = 0.0f;

		for (int j=0; j<3; j++) {
			(*set_extent)[i] += WWMath::Fabs(Row[i][j] * extent[j]);
		}
	}
}
