/*****************************************************************************
  Name : KMLight.c
  Date : August 1998
  Platform : ANSI compatible
 
  * Description : 
  This file is part of the KAMUI TOOLS library (KMTools.lib) used
  with the SDK demos for PowerVR series 2.

  Set of functions used to perform 3D lighting.


  Copyright : 1998 by VideoLogic Limited. All rights reserved.
******************************************************************************/
#include <Machine.h>

#include "KMTools.h"

/*************************************/
/*  ALIGNED GLOBALS FOR SH4 ROUTINES */
/*************************************/

unsigned long	Buffer6[10];
#define	pNormalVector		((float *)(((unsigned long)Buffer6+7)&0xFFFFFFF8))

unsigned long	Buffer7[10];
#define	pLightDirection		((float *)(((unsigned long)Buffer7+7)&0xFFFFFFF8))

unsigned long	Buffer8[10];
#define	pLightDirection1		((float *)(((unsigned long)Buffer8+7)&0xFFFFFFF8))



/*******************************************************************************
 * Function Name  : KMLightSmoothShade
 * Inputs		  : *pMatrix, nNumberOfVertices, *pNormals, 
 *					LightVector, dwLightColour
 * Output		  : *pTLVertex
 * Globals used   : None
 * Description    : Smooth shade vertices.
 *					The WORLD matrix is passed as a parameter (pMatrix) and the
 *					inverse matrix is computed in the function to perform the
 *					shading.
 *					The resulting colour and specular are set in the
 *					pTLVertex array.
 *					Of course normal vectors must be normalised.
 *					You can set the Alpha value of the vertices by passing
 *					dwLightColour with an ALPHA component.
 *					Vertex fog is set to 0.
 *					To compute specular color set the bUseSpecular flag to TRUE.
 *					The Vfog parameter in the specular color is not changed here.
 *					
 *******************************************************************************/
void KMLightSmoothShade(MATRIX *pMatrix, 
				 int nNumberOfVertices, float *pNormals, KMVERTEX_03 *pTLVertex, 
				 km_vector LightVector, DWORD dwLightColour, int bUseSpecular)
{

	MATRIX		Inverse;
	float		TLightVector[3];
	float		LVector[3];
	km_vector	*pNormalPointer;
	KMVERTEX_03	*pTLVertexPointer;
	BYTE		Red, Green, Blue, Alpha;
	BYTE		r, g, b, w;
	float		fCosine;
	int			i;
	int         bIsWhiteLight;
	KMDWORD     nFog;

	/* Copy world matrix and compute inverse matrix */
	KMMatrixCopy(&Inverse, pMatrix);
	KMMatrixInverse(&Inverse);

	/* Invert light vector */
	LVector[0]=-LightVector[0];
	LVector[1]=-LightVector[1];
	LVector[2]=-LightVector[2];
	
	/* Normalize light vector in case it is not */
	KMMatrixNormalize(LVector);

	/* Transform the light vector with inverse world matrix.
	   Note : Don't include Inverse._4X because LightVector = TO - FROM. 
	   FROM is always (0, 0, 0), so transformed FROM would be equal to
	   (Inverse._41, Inverse._42, Inverse._43). Thus by doing TO - FROM, Inverse._4X
	   would be subtracted with themselves, hence yielding zero */
	TLightVector[0]=LVector[0]*Inverse._11 + 
					LVector[1]*Inverse._21 +
					LVector[2]*Inverse._31;

	TLightVector[1]=LVector[0]*Inverse._12 + 
					LVector[1]*Inverse._22 +
					LVector[2]*Inverse._32;

	TLightVector[2]=LVector[0]*Inverse._13 + 
					LVector[1]*Inverse._23 +
					LVector[2]*Inverse._33;
	

	/* Get Red, Green and Blue components of light */
	Red=	GET_RED(dwLightColour);
	Green=	GET_GREEN(dwLightColour);
	Blue=	GET_BLUE(dwLightColour);
	Alpha=	GET_ALPHA(dwLightColour);

	/* If the light is white we can avoid */
	/* 3 multiplications per vertex       */
	if (Red == Green && Green == Blue)
	{
		bIsWhiteLight = TRUE;
		pLightDirection[0] = TLightVector[0] * (float)Red;
		pLightDirection[1] = TLightVector[1] * (float)Red;
		pLightDirection[2] = TLightVector[2] * (float)Red;
		pLightDirection[3] = 0.0f;
	}
	else
	{
		bIsWhiteLight = FALSE;
		pLightDirection[0] = TLightVector[0];
		pLightDirection[1] = TLightVector[1];
		pLightDirection[2] = TLightVector[2];
		pLightDirection[3] = 0.0f;
	}

	/* Get pointers */
	pNormalPointer=(km_vector *)&pNormals[0];
	pTLVertexPointer=&pTLVertex[0];
	
	/* Compute dot product for each vertex */
	for (i=0; i<nNumberOfVertices; i++)
	{
		pNormalVector[0] = *pNormals++;
		pNormalVector[1] = *pNormals++;
		pNormalVector[2] = *pNormals++;
		pNormalVector[3] = 0.0f;

		fCosine= fipr(pNormalVector, pLightDirection);
				
		/* If colour is negative, then make it 0 */
		if (fCosine<0) fCosine=0;

		if (!bIsWhiteLight) /* Coulored light */
		{
			/* Compute Red, Green and Blue colour values */
			r=(BYTE)(fCosine*Red);
			g=(BYTE)(fCosine*Green);
			b=(BYTE)(fCosine*Blue);

			/* Diffuse colour */
			pTLVertex->uBaseRGB.dwPacked = MAKE_RGBA(r, g, b, Alpha);

			/* Compute highlight : set highlight to 0 for any intensity value below 240 */
			/* We keep the original vertex fog value                                    */
			if (bUseSpecular)
			{
			nFog = pTLVertex->uOffsetRGB.dwPacked & 0xFF000000;
			 pTLVertex->uOffsetRGB.dwPacked = nFog | MAKE_RGB( (r>240) ? (r-240)<<4 : 0, (g>240) ? (g-240)<<4 : 0, (b>240) ? (b-240)<<4 : 0);
			}
		}

  		else /* White light */
		{
			w = (BYTE) (fCosine);
			/* Diffuse colour */
			pTLVertex->uBaseRGB.dwPacked = MAKE_RGBA(w, w, w, Alpha);

			/* Compute highlight : set highlight to 0 for any intensity value below 240 */
			/* We keep the original vertex fog value                                    */
			if (bUseSpecular)
			{
			nFog = pTLVertex->uOffsetRGB.dwPacked & 0xFF000000;
			pTLVertex->uOffsetRGB.dwPacked = nFog | MAKE_RGB( (w>240) ? (w-240)<<4 : 0, (w>240) ? (w-240)<<4 : 0, (w>240) ? (w-240)<<4 : 0);
		
			}
		}

		/* Increase vertex count */
		pTLVertex++;
	}

}
/****************************************************************************
 * Function Name  : KMLightSpecular
 * Inputs		  : *pWorldMatrix, nNumberOfVertices, *pNormals, *pVertex,
 *					*pLightVector, *pCameraVector, cDiffuse, cSpecular,
 *					fSpecularPower
 * Output		  : *pTLVertex
 * Globals used	  : None
 * Description	  *	Smooth shade vertices with specular highlights.
 *				  *	The WORLD matrix is passed as a parameter (pWorldMatrix) and the
 *					inverse matrix is computed in the function to perform the
 *					shading, colour cDiffuse.
 *				  *	The camera World position (pCamWorldPos, camera 'from'
 *					vector) is used for specular highlight intensity (colour
 *					cSpecular). The alternative would be to use the inverse
 *					view matrix to back transform [0, 0, 0] to world
 *					co-ordinates and use that as the camera position, but the
 *					transformation functions build the view matrix internally.
 *				  *	The resulting colours are asserted in the pTLVertex array.
 *				  *	Of course normal vectors must be normalised.
 *				  *	You can set the Alpha value of the vertices by passing
 *					cDiffuse with an ALPHA component.
 *				  *	Vertex fog (alpha channel in the pTLVertex specular
 *					component) is unchanged.
 ****************************************************************************/
void KMLightSpecular(const MATRIX *pWorldMatrix,
					   const int nNumberOfVertices, const float *pNormals,
					   const float *pVertex, KMVERTEX_03 *pTLVertex,
					   const km_vector pLightVector, const km_vector pCamWorldPos,
					   const DWORD cDiffuse, const DWORD cSpecular,
					   const float fSpecularPower)
{
	km_vector	pvNormals;
	km_vector	pvVertex;
	km_vector	vTLight, vTView, vHalfway;
	int			nLoop;
	float		fCosine;
	MATRIX		mInverse;
	DWORD		dwDiffuseR, dwDiffuseG, dwDiffuseB, dwDiffuseA;	/* Diffuse colour components */
	DWORD		dwSpecularR, dwSpecularG, dwSpecularB;			/* Specular colour components */
	

	/* extract colour components */
	dwDiffuseR = (cDiffuse>>16) & 0x000000FF;
	dwDiffuseG = (cDiffuse>>8) & 0x000000FF;
	dwDiffuseB = (cDiffuse) & 0x000000FF;
	dwDiffuseA = cDiffuse & 0xFF000000;		/* notice this is not shifted into LSByte! */

	dwSpecularR = (cSpecular>>16) & 0x000000FF;
	dwSpecularG = (cSpecular>>8) & 0x000000FF;
	dwSpecularB = (cSpecular) & 0x000000FF;

	/* Copy world matrix and compute inverse matrix */
	KMMatrixCopy(&mInverse, pWorldMatrix);
	KMMatrixInverse(&mInverse);

	/* Transform the [inverted] light vector with inverse world matrix.
	   Note : Don't include Inverse._4X because LightVector = TO - FROM. 
	   FROM is always (0, 0, 0), so transformed FROM would be equal to
	   (Inverse._41, Inverse._42, Inverse._43). Thus by doing TO - FROM, Inverse._4X
	   would be subtracted with themselves, hence yielding zero */
	/* negatives effectively invert the light vector */
	vTLight[0] = -(pLightVector[0] * mInverse._11 +
				  pLightVector[1] * mInverse._21 +
				  pLightVector[2] * mInverse._31);

	vTLight[1] = -(pLightVector[0] * mInverse._12 + 
				  pLightVector[1] * mInverse._22 +
				  pLightVector[2] * mInverse._32);

	vTLight[2] = -(pLightVector[0] * mInverse._13 + 
				  pLightVector[1] * mInverse._23 +
				  pLightVector[2] * mInverse._33);

	/* Normalize light vector in case it is not */
	KMMatrixNormalize(vTLight);


	/* back transform camera; world space to model space */
	vTView[0] = pCamWorldPos[0] * mInverse._11 +
			   pCamWorldPos[1] * mInverse._21 +
			   pCamWorldPos[2] * mInverse._31;

	vTView[1] = pCamWorldPos[0] * mInverse._12 + 
			   pCamWorldPos[1] * mInverse._22 +
			   pCamWorldPos[2] * mInverse._32;

	vTView[2] = pCamWorldPos[0] * mInverse._13 + 
			   pCamWorldPos[1] * mInverse._23 +
			   pCamWorldPos[2] * mInverse._33;


	/* for a white-light speedup, this while() would have to be repeated
	** four times, once for each combination of specular & diffuse
	** white/coloured light */
	nLoop = nNumberOfVertices;
	while(nLoop--)
	{
		pvNormals[0] = *(pNormals++);
		pvNormals[1] = *(pNormals++);
		pvNormals[2] = *(pNormals++);
		
		pvVertex[0] = *(pVertex++);
		pvVertex[1] = *(pVertex++);
		pvVertex[2] = *(pVertex++);
		
		/* Diffuse colour */
		/* fCosine = cosinus angle between light vector and normal vector */
		fCosine = KMMatrixDotProduct(pvNormals, vTLight);

		/* If cosine is negative, then make it 0 */
		if(fCosine > 0)
		{
			/* Compute Red, Green and Blue colour values; pass through alpha */
			pTLVertex->uBaseRGB.dwPacked = dwDiffuseA | MAKE_RGB(
				(DWORD)(dwDiffuseR * fCosine),
				(DWORD)(dwDiffuseG * fCosine),
				(DWORD)(dwDiffuseB * fCosine));
		}
		else
		{
			/* normal points away from light, no colour at all */
			pTLVertex->uBaseRGB.dwPacked = dwDiffuseA;
		}


		/* Specular colour
		** Method:
		**   New View vector for each vertex. 'halfway' vector between Light
		**   vector & View vector [addition & normalization]. Dot product of
		**   this and vertex normal, to power of fSpecularPower, gives
		**   specular colour factor */

		/* Vector: cam-vertex [View vector for vertex] */
		vHalfway[0] = vTView[0] - pvVertex[0];
		vHalfway[1] = vTView[1] - pvVertex[1];
		vHalfway[2] = vTView[2] - pvVertex[2];

		/* Normalize view vector */
		KMMatrixNormalize(vHalfway);

		/* & this makes halfway vector */
		vHalfway[0] += vTLight[0];
		vHalfway[1] += vTLight[1];
		vHalfway[2] += vTLight[2];

		/* Normalize halfway vector */
		KMMatrixNormalize(vHalfway);

		/* fCosine = cosinus angle between halfway-vector and normal-vector */
		fCosine = KMMatrixDotProduct(pvNormals, vHalfway);

		/* If cosine is negative, then make it 0 */
		if(fCosine > 0)
		{
			fCosine = (float)pow(fCosine, fSpecularPower);

			/* Compute Red, Green and Blue colour values; Save vertex fog */
			pTLVertex->uOffsetRGB.dwPacked = (pTLVertex->uOffsetRGB.dwPacked & 0xFF000000) | MAKE_RGB(
				(DWORD)(dwSpecularR * fCosine),
				(DWORD)(dwSpecularG * fCosine),
				(DWORD)(dwSpecularB * fCosine)
			);
		}
		else
		{
			/* no specular; save vertex fog value */
			pTLVertex->uOffsetRGB.dwPacked &= 0xFF000000;
		}

		/* Increase vertex count */
		pTLVertex++;
	}
}


