/*
 * Copyright (c) 1992,1993-1995 Argonaut Technologies Limited. All rights reserved.
 *
 * $Id: matrix34.c 1.7 1998/08/13 11:56:03 jon Exp $
 * $Locker: $
 *
 * A very unoptimised set of transforms - these should each
 * be re-done for the sparse multiplication.
 *
 * 3x4 Matrices
 *
 */
#define NO_INLINE_MATH

#include "brender.h"
#include "shortcut.h"
#include "brassert.h"

BR_RCS_ID("$Id: matrix34.c 1.7 1998/08/13 11:56:03 jon Exp $")


/*
 * A = B
 */
void BR_PUBLIC_ENTRY BrMatrix34Copy(br_matrix34 *A, br_matrix34 *B)
{
	UASSERT_MESSAGE("Destination matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Source matrix is NULL", B != NULL);

	A(0,0) = B(0,0);
	A(0,1) = B(0,1);
	A(0,2) = B(0,2);

	A(1,0) = B(1,0);
	A(1,1) = B(1,1);
	A(1,2) = B(1,2);

	A(2,0) = B(2,0);
	A(2,1) = B(2,1);
	A(2,2) = B(2,2);

	A(3,0) = B(3,0);
	A(3,1) = B(3,1);
	A(3,2) = B(3,2);
}

/*
 * A = B*C
 */
void BR_PUBLIC_ENTRY BrMatrix34Mul(br_matrix34 *A, br_matrix34 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Left Hand Source matrix is NULL", B != NULL);
	UASSERT_MESSAGE("Right Hand Source matrix is NULL", C != NULL);

	A(0,0) = BR_MAC3(B(0,0),C(0,0), B(0,1),C(1,0), B(0,2),C(2,0));
	A(0,1) = BR_MAC3(B(0,0),C(0,1), B(0,1),C(1,1), B(0,2),C(2,1));
	A(0,2) = BR_MAC3(B(0,0),C(0,2), B(0,1),C(1,2), B(0,2),C(2,2));

	A(1,0) = BR_MAC3(B(1,0),C(0,0), B(1,1),C(1,0), B(1,2),C(2,0));
	A(1,1) = BR_MAC3(B(1,0),C(0,1), B(1,1),C(1,1), B(1,2),C(2,1));
	A(1,2) = BR_MAC3(B(1,0),C(0,2), B(1,1),C(1,2), B(1,2),C(2,2));

	A(2,0) = BR_MAC3(B(2,0),C(0,0), B(2,1),C(1,0), B(2,2),C(2,0));
	A(2,1) = BR_MAC3(B(2,0),C(0,1), B(2,1),C(1,1), B(2,2),C(2,1));
	A(2,2) = BR_MAC3(B(2,0),C(0,2), B(2,1),C(1,2), B(2,2),C(2,2));

	A(3,0) = BR_MAC3(B(3,0),C(0,0), B(3,1),C(1,0), B(3,2),C(2,0)) + C(3,0);
	A(3,1) = BR_MAC3(B(3,0),C(0,1), B(3,1),C(1,1), B(3,2),C(2,1)) + C(3,1);
	A(3,2) = BR_MAC3(B(3,0),C(0,2), B(3,1),C(1,2), B(3,2),C(2,2)) + C(3,2);

}

void BR_PUBLIC_ENTRY BrMatrix34Identity(br_matrix34 *mat)
{
	UASSERT_MESSAGE("Destination matrix is NULL", mat != NULL);

	M(0,0) = S1; M(0,1) = S0; M(0,2) = S0;
	M(1,0) = S0; M(1,1) = S1; M(1,2) = S0;
	M(2,0) = S0; M(2,1) = S0; M(2,2) = S1;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 *	 Ú		  ¿
 *	 ³ 1    0    0    0 ³
 *	 ³		  ³
 *	 ³ 0  cosé  siné  0 ³
 * Rx(é) = ³		  ³
 *	 ³ 0 -siné  cosé  0 ³
 *	 ³		  ³
 *	 ³ 0    0    0    1 ³
 *	 À		  Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34RotateX(br_matrix34 *mat, br_angle rx)
{
	br_scalar s = BR_SIN(rx);
	br_scalar c = BR_COS(rx);

	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) = S1; M(0,1) = S0; M(0,2) = S0;
	M(1,0) = S0; M(1,1) =  c; M(1,2) =  s;
	M(2,0) = S0; M(2,1) = -s; M(2,2) =  c;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 *	 Ú		  ¿
 *	 ³ cosé  0 -siné  0 ³
 *	 ³		  ³
 *	 ³  0    1   0    0 ³
 * Ry(é) = ³		  ³
 *	 ³ siné  0  cosé  0 ³
 *	 ³		  ³
 *	 ³  0    0   0    1 ³
 *	 À		  Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34RotateY(br_matrix34 *mat, br_angle ry)
{
	br_scalar s = BR_SIN(ry);
	br_scalar c = BR_COS(ry);

	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) =  c; M(0,1) = S0; M(0,2) = -s;
	M(1,0) = S0; M(1,1) = S1; M(1,2) = S0;
	M(2,0) =  s; M(2,1) = S0; M(2,2) =  c;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 *	 Ú		   ¿
 *	 ³  cosé  siné  0  0 ³
 *	 ³		   ³
 *	 ³ -siné  cosé  0  0 ³
 * Ry(é) = ³		   ³
 *	 ³   0     0    1  0 ³
 *	 ³		   ³
 *	 ³   0     0    0  1 ³
 *	 À		   Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34RotateZ(br_matrix34 *mat, br_angle rz)
{
	br_scalar s = BR_SIN(rz);
	br_scalar c = BR_COS(rz);

	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) =  c; M(0,1) =  s; M(0,2) = S0;
	M(1,0) = -s; M(1,1) =  c; M(1,2) = S0;
	M(2,0) = S0; M(2,1) = S0; M(2,2) = S1;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 * Rotation about an arbitary (normalised) axis
 */
void BR_PUBLIC_ENTRY BrMatrix34Rotate(br_matrix34 *mat, br_angle r, br_vector3 *a)
{
	br_scalar t,s,c;
	br_scalar txy,txz,tyz,sx,sy,sz;

	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);
	UASSERT_MESSAGE("Vector is NULL", a != NULL);

	s = BR_SIN(r);
	c = BR_COS(r);
	t = S1-c;

	txy = BR_MUL(t,a->v[X]);
	txz = BR_MUL(txy,a->v[Z]);
	txy = BR_MUL(txy,a->v[Y]);
	tyz = BR_MUL(t,BR_MUL(a->v[Y],a->v[Z]));

	sx = BR_MUL(s,a->v[X]);
	sy = BR_MUL(s,a->v[Y]);
	sz = BR_MUL(s,a->v[Z]);

	M(0,0) = BR_MUL(t,BR_SQR(a->v[X]))+c; M(0,1) = txy+sz; M(0,2) = txz-sy;
	M(1,0) = txy-sz; M(1,1) = BR_MUL(t,BR_SQR(a->v[Y]))+c; M(1,2) = tyz+sx;
	M(2,0) = txz+sy; M(2,1) = tyz-sx; M(2,2) = BR_MUL(t,BR_SQR(a->v[Z]))+c;

	M(3,0) = M(3,1) = M(3,2) = S0;
}

/*
 *	       Ú		¿
 *	       ³  1   0   0   0 ³
 *	       ³		³
 *	       ³  0   1   0   0 ³
 * T(dx,dy,dz) = ³		³
 *	       ³  0   0   1   0 ³
 *	       ³		³
 *	       ³  dx  dy  dz  1 ³
 *	       À		Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34Translate(br_matrix34 *mat, br_scalar dx, br_scalar dy, br_scalar dz)
{
	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) = S1; M(0,1) = S0; M(0,2) = S0;
	M(1,0) = S0; M(1,1) = S1; M(1,2) = S0;
	M(2,0) = S0; M(2,1) = S0; M(2,2) = S1;
	M(3,0) = dx; M(3,1) = dy; M(3,2) = dz;
}

/*
 *	       Ú		¿
 *	       ³  sx  0   0   0 ³
 *	       ³		³
 *	       ³  0   sy  0   0 ³
 * S(sx,sy,sz) = ³		³
 *	       ³  0   0   sz  0 ³
 *	       ³		³
 *	       ³  0   0   0   1 ³
 *	       À		Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34Scale(br_matrix34 *mat, br_scalar sx, br_scalar sy, br_scalar sz)
{
	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) = sx; M(0,1) = S0; M(0,2) = S0;
	M(1,0) = S0; M(1,1) = sy; M(1,2) = S0;
	M(2,0) = S0; M(2,1) = S0; M(2,2) = sz;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 *		 Ú		¿
 *		 ³  1   sy  sz  0 ³
 *		 ³		³
 *		 ³  0   1   0   0 ³
 * ShearX(sy,sz) = ³		³
 *		 ³  0   0   1   0 ³
 *		 ³		³
 *		 ³  0   0   0   1 ³
 *		 À		Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34ShearX(br_matrix34 *mat, br_scalar sy, br_scalar sz)
{
	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) = S1; M(0,1) = sy; M(0,2) = sz;
	M(1,0) = S0; M(1,1) = S1; M(1,2) = S0;
	M(2,0) = S0; M(2,1) = S0; M(2,2) = S1;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 *		 Ú		¿
 *		 ³  1   0   0   0 ³
 *		 ³		³
 *		 ³  sx  1   sz  0 ³
 * ShearY(sx,sz) = ³		³
 *		 ³  0   0   1   0 ³
 *		 ³		³
 *		 ³  0   0   0   1 ³
 *		 À		Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34ShearY(br_matrix34 *mat, br_scalar sx, br_scalar sz)
{
	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) = S1; M(0,1) = S0; M(0,2) = S0;
	M(1,0) = sx; M(1,1) = S1; M(1,2) = sz;
	M(2,0) = S0; M(2,1) = S0; M(2,2) = S1;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 *		 Ú		¿
 *		 ³  1   0   0   0 ³
 *		 ³		³
 *		 ³  0   1   0   0 ³
 * ShearZ(sx,sy) = ³		³
 *		 ³  sx  sy  1   0 ³
 *		 ³		³
 *		 ³  0   0   0   1 ³
 *		 À		Ù
 */
void BR_PUBLIC_ENTRY BrMatrix34ShearZ(br_matrix34 *mat, br_scalar sx, br_scalar sy)
{
	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	M(0,0) = S1; M(0,1) = S0; M(0,2) = S0;
	M(1,0) = S0; M(1,1) = S1; M(1,2) = S0;
	M(2,0) = sx; M(2,1) = sy; M(2,2) = S1;
	M(3,0) = S0; M(3,1) = S0; M(3,2) = S0;
}

/*
 * From Graphics Gems II - Fast Matrix Inversion by Kevin Wu (pp. 342)
 *
 * This should be extended to work with flags asociated with each transform.
 *
 *
 * Computes the inverse of a 3D affine matrix; i.e. a matrix with a dimen-
 * sionality of 4 where the right column has the entries (0, 0, 0, 1).
 *
 * This procedure treats the 4 by 4 matrix as a block matrix and
 * calculates the inverse of one submatrix for a significant perform-
 * ance improvement over a general procedure that can invert any non-
 * singular matrix:
 *	  --	--	  --	  --
 *	  |	  | -1       |    -1      |
 *	  | A      0 |	  |   A      0 |
 *    -1    |	  |	  |	    |
 *   M   =  |	  |     =    |     -1     |
 *	  | C      1 |	  | -C A     1 |
 *	  |	  |	  |	    |
 *	  --	--	  --	  --
 *
 *  where     M is a 4 by 4 matrix,
 *	    A is the 3 by 3 ur left submatrix of M,
 *	    C is the 1 by 3 lower left submatrix of M.
 *
 * Input:
 *   A   - 3D affine matrix
 *
 * Output:
 *   B  - inverse of 3D affine matrix
 *   
 * Returned value:
 *   determinant of matrix
 */
#if 0

br_scalar BR_PUBLIC_ENTRY BrMatrix34Inverse(br_matrix34 *B, br_matrix34 *A)
{
    br_scalar    idet,det;
    br_scalar    pos, neg, temp;

#define ACCUMULATE if (temp >= S0) pos += temp; else neg += temp;

#define PRECISION_LIMIT BR_SCALAR(1.0e-15)

#define ABS(a)		(((a)<0) ? -(a) : (a))

	UASSERT_MESSAGE("Destination Matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Source Matrix is NULL", B != NULL);
	UASSERT_MESSAGE("Source Matrix may not be Destination", A != B);

    /*
     * Calculate the determinant of submatrix A and determine if the
     * the matrix is singular as limited by the double precision
     * floating-point data representation.
     */
    pos = neg = S0;
    temp =  BR_MUL(BR_MUL(A(0,0) , A(1,1)) , A(2,2));
    ACCUMULATE
    temp =  BR_MUL(BR_MUL(A(0,1) , A(1,2)) , A(2,0));
    ACCUMULATE
    temp =  BR_MUL(BR_MUL(A(0,2) , A(1,0)) , A(2,1));
    ACCUMULATE
    temp = -BR_MUL(BR_MUL(A(0,2) , A(1,1)) , A(2,0));
    ACCUMULATE
    temp = -BR_MUL(BR_MUL(A(0,1) , A(1,0)) , A(2,2));
    ACCUMULATE
    temp = -BR_MUL(BR_MUL(A(0,0) , A(1,2)) , A(2,1));
    ACCUMULATE
    det = pos + neg;

    /*
	 * Is the submatrix A singular?
	 */
    if(ABS(det) <= BR_SCALAR_EPSILON*2)
		return S0;

	if((ABS(BR_DIV(det,(pos - neg))) < PRECISION_LIMIT)) {
	/*
		 * Matrix M has no inverse
		 */
		return S0;
	}

   /*
	* Calculate inverse(A) = adj(A) / det(A)
	*/
    idet = BR_DIV(S1,det);

    B(0,0) =  BR_MUL(BR_MAC2(A(1,1),A(2,2), -A(1,2),A(2,1)),idet);
    B(1,0) = -BR_MUL(BR_MAC2(A(1,0),A(2,2), -A(1,2),A(2,0)),idet);
    B(2,0) =  BR_MUL(BR_MAC2(A(1,0),A(2,1), -A(1,1),A(2,0)),idet);
    B(0,1) = -BR_MUL(BR_MAC2(A(0,1),A(2,2), -A(0,2),A(2,1)),idet);
    B(1,1) =  BR_MUL(BR_MAC2(A(0,0),A(2,2), -A(0,2),A(2,0)),idet);
    B(2,1) = -BR_MUL(BR_MAC2(A(0,0),A(2,1), -A(0,1),A(2,0)),idet);
    B(0,2) =  BR_MUL(BR_MAC2(A(0,1),A(1,2), -A(0,2),A(1,1)),idet);
    B(1,2) = -BR_MUL(BR_MAC2(A(0,0),A(1,2), -A(0,2),A(1,0)),idet);
    B(2,2) =  BR_MUL(BR_MAC2(A(0,0),A(1,1), -A(0,1),A(1,0)),idet);

    /*
	 * Calculate -C * inverse(A)
	 */
    B(3,0) = -BR_MAC3(A(3,0),B(0,0), A(3,1),B(1,0), A(3,2),B(2,0));
    B(3,1) = -BR_MAC3(A(3,0),B(0,1), A(3,1),B(1,1), A(3,2),B(2,1));
    B(3,2) = -BR_MAC3(A(3,0),B(0,2), A(3,1),B(1,2), A(3,2),B(2,2));

    return det;
}
#endif


#if 1

#define AF(x,y) (AF[x][y])
#define BF(x,y) (BF[x][y])

/*
 * Use float intermediates for invertion
 */
br_scalar BR_PUBLIC_ENTRY BrMatrix34Inverse(br_matrix34 *B, br_matrix34 *A)
{
    double	idet,det;
    double	pos, neg, temp;
	double	AF[4][3], BF[4][3];
	int i;

#define ACCUMULATE if (temp >= 0.0) pos += temp; else neg += temp;
#define PRECISION_LIMIT BR_SCALAR(1.0e-15)
#define ABS(a)		(((a)<0) ? -(a) : (a))

	UASSERT_MESSAGE("Destination Matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Source Matrix is NULL", B != NULL);
	UASSERT_MESSAGE("Source Matrix may not be Destination", A != B);
	/*
	 * Convert input to floats
	 */
	for(i=0; i < 4; i++) {
		AF(i,0) = BrScalarToFloat(A(i,0));
		AF(i,1) = BrScalarToFloat(A(i,1));
		AF(i,2) = BrScalarToFloat(A(i,2));
	}

    /*
     * Calculate the determinant of submatrix A and determine if the
     * the matrix is singular as limited by the double precision
     * floating-point data representation.
     */
    pos = neg = 0.0F;
    temp =  AF(0,0) * AF(1,1) * AF(2,2);
    ACCUMULATE
    temp =  AF(0,1) * AF(1,2) * AF(2,0);
    ACCUMULATE
    temp =  AF(0,2) * AF(1,0) * AF(2,1);
    ACCUMULATE
    temp = -AF(0,2) * AF(1,1) * AF(2,0);
    ACCUMULATE
    temp = -AF(0,1) * AF(1,0) * AF(2,2);
    ACCUMULATE
    temp = -AF(0,0) * AF(1,2) * AF(2,1);
    ACCUMULATE
    det = pos + neg;

    /*
	 * Is the submatrix A singular?
	 *
	 * Not sure about this - using double precision as an intermediate
	 * allows more matrices to be inverted but when they are stored back
	 * how can we determine if they will become singular?
	 */
    if(ABS(det) <= PRECISION_LIMIT)
		return S0;

	if((ABS(det/(pos - neg)) < PRECISION_LIMIT)) {
		/*
		 * Matrix M has no inverse
		 */
		return S0;
	}

   /*
	* Calculate inverse(A) = adj(A) / det(A)
	*/
    idet = 1.0F/det;

    BF(0,0) =  (AF(1,1) * AF(2,2) - AF(1,2) * AF(2,1)) * idet;
    BF(1,0) = -(AF(1,0) * AF(2,2) - AF(1,2) * AF(2,0)) * idet;
    BF(2,0) =  (AF(1,0) * AF(2,1) - AF(1,1) * AF(2,0)) * idet;
    BF(0,1) = -(AF(0,1) * AF(2,2) - AF(0,2) * AF(2,1)) * idet;
    BF(1,1) =  (AF(0,0) * AF(2,2) - AF(0,2) * AF(2,0)) * idet;
    BF(2,1) = -(AF(0,0) * AF(2,1) - AF(0,1) * AF(2,0)) * idet;
    BF(0,2) =  (AF(0,1) * AF(1,2) - AF(0,2) * AF(1,1)) * idet;
    BF(1,2) = -(AF(0,0) * AF(1,2) - AF(0,2) * AF(1,0)) * idet;
    BF(2,2) =  (AF(0,0) * AF(1,1) - AF(0,1) * AF(1,0)) * idet;

    /*
	 * Calculate -C * inverse(A)
	 */
    BF(3,0) = -(AF(3,0) * BF(0,0) + AF(3,1) * BF(1,0) + AF(3,2) * BF(2,0));
    BF(3,1) = -(AF(3,0) * BF(0,1) + AF(3,1) * BF(1,1) + AF(3,2) * BF(2,1));
    BF(3,2) = -(AF(3,0) * BF(0,2) + AF(3,1) * BF(1,2) + AF(3,2) * BF(2,2));

	/*
	 * Convert output back to scalars
	 */
	for(i=0; i < 4; i++) {
		B(i,0) = BrFloatToScalar(BF(i,0));
		B(i,1) = BrFloatToScalar(BF(i,1));
		B(i,2) = BrFloatToScalar(BF(i,2));
	}

    return BrFloatToScalar(det);
}
#endif

/*
 * Invert a length preserving matrix
 *
 * Given:
 *	  
 *  A     0
 *
 *  C     1
 *
 * Produce -
 *
 *   T
 *  A     0
 *     T
 * -C A	  1
 *
 */
void BR_PUBLIC_ENTRY BrMatrix34LPInverse(br_matrix34 *A, br_matrix34 *B)
{
	UASSERT_MESSAGE("Destination Matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Source Matrix is NULL", B != NULL);
	UASSERT_MESSAGE("Source Matrix may not be Destination", A != B);

	A(0,0) = B(0,0);
	A(0,1) = B(1,0);
	A(0,2) = B(2,0);

	A(1,0) = B(0,1);
	A(1,1) = B(1,1);
	A(1,2) = B(2,1);

	A(2,0) = B(0,2);
	A(2,1) = B(1,2);
	A(2,2) = B(2,2);

    /*
	 * Calculate -C * inverse(A)
	 */
   	A(3,0) = -BR_MAC3(B(3,0),A(0,0), B(3,1),A(1,0), B(3,2),A(2,0));
    A(3,1) = -BR_MAC3(B(3,0),A(0,1), B(3,1),A(1,1), B(3,2),A(2,1));
    A(3,2) = -BR_MAC3(B(3,0),A(0,2), B(3,1),A(1,2), B(3,2),A(2,2));
}

void BR_PUBLIC_ENTRY BrMatrix34LPNormalise(br_matrix34 *A, br_matrix34 *B)
{
	UASSERT_MESSAGE("Destination Matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Source Matrix is NULL", B != NULL);

	BrVector3Normalise((br_vector3 *)A->m[Z],(br_vector3 *)B->m[Z]);
	BrVector3Cross((br_vector3 *)A->m[X],(br_vector3 *)B->m[Y],(br_vector3 *)A->m[Z]);
	BrVector3Normalise((br_vector3 *)A->m[X],(br_vector3 *)A->m[X]);
	BrVector3Cross((br_vector3 *)A->m[Y],(br_vector3 *)A->m[Z],(br_vector3 *)A->m[X]);

	A(3,0) = B(3,0);
	A(3,1) = B(3,1);
	A(3,2) = B(3,2);	
}


/*
 * From Graphic Gems II - The Rolling Ball, Andrew J. Hanson (pp. 51)
 *
 * given incremental device scalars, dx,dy, and radius of ball -
 *
 * Find axis of rotation:
 *
 * 	dr = sqrt(dx^2+dy^2)
 * 	nx = -dy/dr
 * 	ny = +dx/dr
 * 	nz = 0
 *
 * Find angle to rotate about n:
 *
 * cos(a) = radius/sqrt(radius^2 + dr^2)
 * sin(a) = dr/sqrt(radius^2 + dr^2)
 *
 * Now plug these values into a rotation by an angle about a vector
 */

void BR_PUBLIC_ENTRY BrMatrix34RollingBall(br_matrix34 *mat, int dx,int dy, int radius)
{
	br_scalar nx,ny;
	br_scalar ca,sa;
	br_scalar dr,h;

	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);

	dr = BR_LENGTH2(BrIntToScalar(dx),BrIntToScalar(dy));

	if(dr == BR_SCALAR(0.0)) {
		BrMatrix34Identity(mat);
		return;
	}

	nx = -BR_DIV(BrIntToScalar(dy),dr);
	ny =  BR_DIV(BrIntToScalar(dx),dr);

	h = BR_LENGTH2(BrIntToScalar(radius),dr);
	ca = BR_DIV(BrIntToScalar(radius),h);
	sa = BR_DIV(dr,h);

	M(0,0) = ca+BR_MUL(BR_MUL(nx,nx),(S1-ca));
	M(1,0) = BR_MUL(BR_MUL(nx,ny) ,(S1-ca));
	M(2,0) = BR_MUL(ny,sa);

	M(0,1) = M(1,0);
	M(1,1) = ca+BR_MUL(BR_MUL(ny,ny),(S1-ca));
	M(2,1) = -BR_MUL(nx,sa);

	M(0,2) = -M(2,0);
	M(1,2) = -M(2,1);
	M(2,2) = ca;

	M(3,0) = S0;
	M(3,1) = S0;
	M(3,2) = S0;
}

/*
 * Find the transform that maps a 2 unit cube centred on origin to the given bounding box
 */
br_matrix34 * BR_PUBLIC_ENTRY BrBoundsToMatrix34( br_matrix34 *mat, br_bounds *bounds)
{
	int i;
	br_vector3 tr,sc;

	UASSERT_MESSAGE("Matrix is NULL", mat != NULL);
	UASSERT_MESSAGE("Bounds is NULL", bounds != NULL);

	/*
	 * Find the translation
	 */
	for(i=0; i< 3; i++)
		tr.v[i] = (BR_CONST_DIV(bounds->max.v[i],2) +
				   BR_CONST_DIV(bounds->min.v[i],2));

	/*
	 * Find the scale
	 */
	for(i=0; i< 3; i++)
		if(bounds->max.v[i] != bounds->min.v[i])
			sc.v[i] = BR_CONST_DIV(bounds->max.v[i],2) -
					  BR_CONST_DIV(bounds->min.v[i],2);
		else
			sc.v[i] = BR_SCALAR(1.0);

	BrMatrix34Scale(mat,sc.v[0],sc.v[1],sc.v[2]);
	BrMatrix34PostTranslate(mat,tr.v[0],tr.v[1],tr.v[2]);

	return mat;
}

void BR_PUBLIC_ENTRY BrMatrix34Copy4(br_matrix34 *A, br_matrix4 *B)
{
	UASSERT_MESSAGE("Destination Matrix is NULL", A != NULL);
	UASSERT_MESSAGE("Source Matrix is NULL", B != NULL);

	A(0,0) = B(0,0); A(0,1) = B(0,1); A(0,2) = B(0,2);
	A(1,0) = B(1,0); A(1,1) = B(1,1); A(1,2) = B(1,2);
	A(2,0) = B(2,0); A(2,1) = B(2,1); A(2,2) = B(2,2);
	A(3,0) = B(3,0); A(3,1) = B(3,1); A(3,2) = B(3,2);
}

/*
 * Transform a bounding box into another bounding box
 *
 * based on "Transforming Axis-Aligned Bounding Boxes" by James Avro -
 * Gems I, page 548
 */
void BR_PUBLIC_ENTRY BrMatrix34ApplyBounds(br_bounds *A, br_bounds *B, br_matrix34 *C)
{
	int i,j;
	br_scalar a,b;

	ASSERT_MESSAGE("NULL pointer to the bounding box", A != NULL);
	ASSERT_MESSAGE("NULL pointer to the bounding box", B != NULL);
	ASSERT(C != NULL);

	/*
	 * Start with translation part
	 */
	A->min.v[0] = A->max.v[0] = C->m[3][0];
	A->min.v[1] = A->max.v[1] = C->m[3][1];
	A->min.v[2] = A->max.v[2] = C->m[3][2];

	/*
	 * Add in extreme values obtained by computing the products
	 * of the min and maxes with the elements of the i'th row
	 * of the matrix
	 */
	for( i = 0; i < 3; i++ ) {
	    for( j = 0; j < 3; j++ ) {
			a = BR_MUL(C->m[j][i], B->min.v[j]);
			b = BR_MUL(C->m[j][i], B->max.v[j]);
			if( a < b ) {
				A->min.v[i] += a; 
				A->max.v[i] += b;
			} else {
				A->min.v[i] += b; 
				A->max.v[i] += a;
			}
		}
	}
}

/*
 * [a b c d] = [ e f g 0 ] . transpose(M)
 * (unit vector)
 *
 * (Private function)
 */
void BrMatrix34TApplyFV(br_vector3 *A, br_fvector3 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);

	A->v[0] = BR_FMAC3(B->v[0],C(0,0), B->v[1],C(0,1), B->v[2],C(0,2));
	A->v[1] = BR_FMAC3(B->v[0],C(1,0), B->v[1],C(1,1), B->v[2],C(1,2));
	A->v[2] = BR_FMAC3(B->v[0],C(2,0), B->v[1],C(2,1), B->v[2],C(2,2));
}

/*
 * vec_a = vec_b * mat
 */
void BR_PUBLIC_ENTRY BrMatrix34Apply(br_vector3 *A, br_vector4 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);
	UASSERT_MESSAGE("Source vector may not be destination", A != (br_vector3 *) B);

	A->v[0] = BR_MAC4(B->v[0],C(0,0), B->v[1],C(1,0), B->v[2],C(2,0), B->v[3],C(3,0));
	A->v[1] = BR_MAC4(B->v[0],C(0,1), B->v[1],C(1,1), B->v[2],C(2,1), B->v[3],C(3,1));
	A->v[2] = BR_MAC4(B->v[0],C(0,2), B->v[1],C(1,2), B->v[2],C(2,2), B->v[3],C(3,2));
}

/*
 * [a b c ] = [ e f g ] . M
 */
void BR_PUBLIC_ENTRY BrMatrix34ApplyP(br_vector3 *A, br_vector3 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);
	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);

	A->v[0] = BR_MAC3(B->v[0],C(0,0), B->v[1],C(1,0), B->v[2],C(2,0)) + C(3,0);
	A->v[1] = BR_MAC3(B->v[0],C(0,1), B->v[1],C(1,1), B->v[2],C(2,1)) + C(3,1);
	A->v[2] = BR_MAC3(B->v[0],C(0,2), B->v[1],C(1,2), B->v[2],C(2,2)) + C(3,2);
}

/*
 * [a b c d] = [ e f g 0 ] . M
 */
void BR_PUBLIC_ENTRY BrMatrix34ApplyV(br_vector3 *A, br_vector3 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);
	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);

	A->v[0] = BR_MAC3(B->v[0],C(0,0), B->v[1],C(1,0), B->v[2],C(2,0));
	A->v[1] = BR_MAC3(B->v[0],C(0,1), B->v[1],C(1,1), B->v[2],C(2,1));
	A->v[2] = BR_MAC3(B->v[0],C(0,2), B->v[1],C(1,2), B->v[2],C(2,2));
}

/*
 * vec_a = vec_b * transpose(M)
 */
void BR_PUBLIC_ENTRY BrMatrix34TApply(br_vector4 *A, br_vector4 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);
	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);

	A->v[0] = BR_MAC3(B->v[0],C(0,0), B->v[1],C(0,1), B->v[2],C(0,2));
	A->v[1] = BR_MAC3(B->v[0],C(1,0), B->v[1],C(1,1), B->v[2],C(1,2));
	A->v[2] = BR_MAC3(B->v[0],C(2,0), B->v[1],C(2,1), B->v[2],C(2,2));
	A->v[3] = BR_ADD(BR_MAC3(B->v[0],C(3,0), B->v[1],C(3,1), B->v[2],C(3,2)), B->v[3]);
}

/*
 * [a b c] = [ e f g] . transpose(M)
 */
void BR_PUBLIC_ENTRY BrMatrix34TApplyP(br_vector3 *A, br_vector3 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);
	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);

	A->v[0] = BR_MAC3(B->v[0],C(0,0), B->v[1],C(0,1), B->v[2],C(0,2));
	A->v[1] = BR_MAC3(B->v[0],C(1,0), B->v[1],C(1,1), B->v[2],C(1,2));
	A->v[2] = BR_MAC3(B->v[0],C(2,0), B->v[1],C(2,1), B->v[2],C(2,2));
}

/*
 * [a b c d] = [ e f g 0 ] . transpose(M)
 */
void BR_PUBLIC_ENTRY BrMatrix34TApplyV(br_vector3 *A, br_vector3 *B, br_matrix34 *C)
{
	UASSERT_MESSAGE("Destination Vector is NULL", A != NULL);
	UASSERT_MESSAGE("Source Vector is NULL", B != NULL);
	UASSERT_MESSAGE("Transform Matrix is NULL", C != NULL);
	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);	UASSERT_MESSAGE("Source and Destination vector may not be the same.", A != B);

	A->v[0] = BR_MAC3(B->v[0],C(0,0), B->v[1],C(0,1), B->v[2],C(0,2));
	A->v[1] = BR_MAC3(B->v[0],C(1,0), B->v[1],C(1,1), B->v[2],C(1,2));
	A->v[2] = BR_MAC3(B->v[0],C(2,0), B->v[1],C(2,1), B->v[2],C(2,2));
}

/*
 * XXX - Apply vectors of vectors through matrix
 *
 * BrMatrix34ApplyNV()
 * BrMatrix34ApplyNP()
 * BrMatrix34ApplyN()
 */


/*
 * Composite matrix operations -
 * pre and post-multiply with an existing matrix
 */

void BR_PUBLIC_ENTRY BrMatrix34Pre(br_matrix34 *mat , br_matrix34 *A)
{
	br_matrix34 mattmp;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);
	UASSERT_MESSAGE("Pre-multiplying matrix is NULL", A != NULL);

	BrMatrix34Mul(&mattmp,A,mat);
	BrMatrix34Copy(mat,&mattmp);
}

void BR_PUBLIC_ENTRY BrMatrix34Post(br_matrix34 *mat , br_matrix34 *A)
{
	br_matrix34 mattmp;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);
	UASSERT_MESSAGE("Post-multiplying matrix is NULL", A != NULL);

	BrMatrix34Mul(&mattmp,mat,A);
	BrMatrix34Copy(mat,&mattmp);
}

void BR_PUBLIC_ENTRY BrMatrix34PreRotateX(br_matrix34 *mat, br_angle rx)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34RotateX(&mattmp1,rx);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostRotateX(br_matrix34 *mat, br_angle rx)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34RotateX(&mattmp1,rx);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreRotateY(br_matrix34 *mat, br_angle ry)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

    BrMatrix34RotateY(&mattmp1,ry);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostRotateY(br_matrix34 *mat, br_angle ry)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34RotateY(&mattmp1,ry);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreRotateZ(br_matrix34 *mat, br_angle rz)
{			  
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34RotateZ(&mattmp1,rz);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostRotateZ(br_matrix34 *mat, br_angle rz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34RotateZ(&mattmp1,rz);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreRotate(br_matrix34 *mat, br_angle r, br_vector3 *axis)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34Rotate(&mattmp1,r,axis);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostRotate(br_matrix34 *mat, br_angle r, br_vector3 *axis)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34Rotate(&mattmp1,r,axis);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreTranslate(br_matrix34 *mat, br_scalar x, br_scalar y, br_scalar z)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34Translate(&mattmp1,x,y,z);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostTranslate(br_matrix34 *mat, br_scalar x, br_scalar y, br_scalar z)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34Translate(&mattmp1,x,y,z);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreScale(br_matrix34 *mat, br_scalar sx, br_scalar sy, br_scalar sz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34Scale(&mattmp1,sx,sy,sz);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostScale(br_matrix34 *mat, br_scalar sx, br_scalar sy, br_scalar sz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34Scale(&mattmp1,sx,sy,sz);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreShearX(br_matrix34 *mat, br_scalar sy, br_scalar sz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34ShearX(&mattmp1,sy,sz);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostShearX(br_matrix34 *mat, br_scalar sy, br_scalar sz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34ShearX(&mattmp1,sy,sz);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreShearY(br_matrix34 *mat, br_scalar sx, br_scalar sz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34ShearY(&mattmp1,sx,sz);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostShearY(br_matrix34 *mat, br_scalar sx, br_scalar sz)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34ShearY(&mattmp1,sx,sz);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PreShearZ(br_matrix34 *mat, br_scalar sx, br_scalar sy)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34ShearZ(&mattmp1,sx,sy);
	BrMatrix34Mul(&mattmp2,&mattmp1,mat);
	BrMatrix34Copy(mat,&mattmp2);
}

void BR_PUBLIC_ENTRY BrMatrix34PostShearZ(br_matrix34 *mat, br_scalar sx, br_scalar sy)
{
	br_matrix34 mattmp1,mattmp2;

	UASSERT_MESSAGE("Subject matrix is NULL", mat != NULL);

	BrMatrix34ShearZ(&mattmp1,sx,sy);
	BrMatrix34Mul(&mattmp2,mat,&mattmp1);
	BrMatrix34Copy(mat,&mattmp2);
}
