/*
Copyright (C) 1997-2001 Id Software, 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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// r_alias_misc.c: shared model rendering functions

#include "r_local.h"


float	r_avertexnormals[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};

// precalculated dot products for quantized angles
float	r_avertexnormal_dots[SHADEDOT_QUANT][256] =
#include "anormtab.h"
;

float		*shadedots = r_avertexnormal_dots[0];

vec3_t		shadelight;

m_dlight_t	model_dlights[MAX_MODEL_DLIGHTS];
int			model_dlights_num;

float		shellFlowH, shellFlowV;

/*
=================
mirrorValue
=================
*/
float  mirrorValue (float value, qboolean mirrormodel)
{
	if (mirrormodel)
	{
		if (value>1)
			return 0;
		else if (value<0)
			return 1;
		else
			return 1-value;
	}
	return value;
}


#if 0
/*
=================
R_CalcEntAlpha
=================
*/
float R_CalcEntAlpha (float alpha, vec3_t point)
{
	float	baseDist, newAlpha;
	vec3_t	vert_len;

	newAlpha = alpha;

	if (!(currententity->renderfx & RF2_CAMERAMODEL) || !(currententity->flags & RF_TRANSLUCENT))
	{
		newAlpha = max(min(newAlpha, 1.0f), 0.0f);
		return newAlpha;
	}

	baseDist = Cvar_VariableValue("cg_thirdperson_dist");
	if (baseDist < 1)	baseDist = 50;
	VectorSubtract(r_newrefdef.vieworg, point, vert_len);
	newAlpha *= VectorLength(vert_len) / baseDist;
	if (newAlpha > alpha)	newAlpha = alpha;

	newAlpha = max(min(newAlpha, 1.0f), 0.0f);

	return newAlpha;
}
#endif


/*
=================
R_CalcShadowAlpha
=================
*/
#define SHADOW_FADE_DIST 128
float R_CalcShadowAlpha (entity_t *e)
{
	vec3_t	vec;
	float	dist, minRange, maxRange, outAlpha;

	VectorSubtract(e->origin, r_origin, vec);
	dist = VectorLength(vec);

	if (r_newrefdef.fov_y >= 90)
		minRange = r_shadowrange->value;
	else // reduced FOV means longer range
		minRange = r_shadowrange->value * (90/r_newrefdef.fov_y); // this can't be zero
	maxRange = minRange + SHADOW_FADE_DIST;

	if (dist <= minRange) // in range
		outAlpha = r_shadowalpha->value;
	else if (dist >= maxRange) // out of range
		outAlpha = 0.0f;
	else // fade based on distance
		outAlpha = r_shadowalpha->value * (fabs(dist-maxRange)/SHADOW_FADE_DIST);

	return outAlpha;
}


/*
==============
R_ShadowBlend
Draws projection shadow(s)
from stenciled volume
==============
*/
void R_ShadowBlend (float shadowalpha)
{
	if (r_shadows->value != 3)
		return;

	qglPushMatrix();
    qglLoadIdentity ();

	// FIXME: get rid of these
    qglRotatef (-90,  1, 0, 0);	    // put Z going up
    qglRotatef (90,  0, 0, 1);	    // put Z going up

	GL_Disable (GL_ALPHA_TEST);
	GL_Enable (GL_BLEND);
	GL_Disable (GL_DEPTH_TEST);
	GL_DisableTexture(0);

	GL_Enable(GL_STENCIL_TEST);
	qglStencilFunc(GL_NOTEQUAL, 0, 255);
	qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

	rb_vertex = rb_index = 0;
	indexArray[rb_index++] = rb_vertex+0;
	indexArray[rb_index++] = rb_vertex+1;
	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+0;
	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+3;
	VA_SetElem3(vertexArray[rb_vertex], 10, 100, 100);
	VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, shadowalpha);
	rb_vertex++;
	VA_SetElem3(vertexArray[rb_vertex], 10, -100, 100);
	VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, shadowalpha);
	rb_vertex++;
	VA_SetElem3(vertexArray[rb_vertex], 10, -100, -100);
	VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, shadowalpha);
	rb_vertex++;
	VA_SetElem3(vertexArray[rb_vertex], 10, 100, -100);
	VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, shadowalpha);
	rb_vertex++;
	RB_RenderMeshGeneric (false);

	qglPopMatrix();

	GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GL_Disable (GL_BLEND);
	GL_EnableTexture(0);
	GL_Enable (GL_DEPTH_TEST);
	GL_Disable(GL_STENCIL_TEST);
	//GL_Enable (GL_ALPHA_TEST);

	qglColor4f(1,1,1,1);
}


/*
=================
capColorVec
=================
*/
void capColorVec (vec3_t color)
{
	int i;

	for (i=0;i<3;i++)
		color[i] = max(min(color[i], 1.0f), 0.0f);
}

/*
=================
R_SetVertexRGBScale
=================
*/
void R_SetVertexRGBScale (qboolean toggle)
{
	if (!r_rgbscale->value || !glConfig.mtexcombine)
		return;

	if (toggle) // turn on
	{
#if 1
		qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
		qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
		qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, r_rgbscale->value);
		qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
		
		GL_TexEnv(GL_COMBINE_ARB);
#else
		qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
		qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
		qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, r_rgbscale->value);
		qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);

		GL_TexEnv(GL_COMBINE_EXT);
#endif
	}
	else // turn off
	{
		GL_TexEnv(GL_MODULATE);
		qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
	}
}


/*
=================
EnvMapShell
=================
*/
qboolean EnvMapShell (void)
{
	return ( (r_shelltype->value == 2)
		|| (r_shelltype->value == 1 && currententity->alpha == 1.0f) );
}


/*
=================
FlowingShell
=================
*/
qboolean FlowingShell (void)
{
	return (r_shelltype->value == 1 && currententity->alpha != 1.0f);
}


/*
=================
R_SetShellBlend
=================
*/
void R_SetShellBlend (qboolean toggle)
{
	// shells only
	if ( !(currententity->flags & RF_MASK_SHELL) )
		return;

	if (toggle) //turn on
	{
		// Psychospaz's envmapping
		if (EnvMapShell())
		{
			qglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
			qglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

			GL_Bind(glMedia.spheremappic->texnum);

			qglEnable(GL_TEXTURE_GEN_S);
			qglEnable(GL_TEXTURE_GEN_T);
		}
		else if (FlowingShell())
			GL_Bind(glMedia.shelltexture->texnum);
		else
			GL_DisableTexture(0);

		GL_Stencil(true, true);

		shellFlowH =  0.25 * sin(r_newrefdef.time * 0.5 * M_PI);
		shellFlowV = -(r_newrefdef.time / 2.0); 
	}
	else // turn off
	{
		// Psychospaz's envmapping
		if (EnvMapShell())
		{
			qglDisable(GL_TEXTURE_GEN_S);
			qglDisable(GL_TEXTURE_GEN_T);
		}
		else if (FlowingShell())
		{ /*nothing*/ }
		else
			GL_EnableTexture(0);

		GL_Stencil(false, true);
	}
}

/*
=================
R_FlipModel
=================
*/
void R_FlipModel (qboolean on, qboolean cullOnly)
{
	extern void MYgluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar );

	if (on)
	{
		if (!cullOnly)
		{
			qglMatrixMode( GL_PROJECTION );
			qglPushMatrix();
			qglLoadIdentity();
			qglScalef( -1, 1, 1 );
			MYgluPerspective( r_newrefdef.fov_y, ( float ) r_newrefdef.width / r_newrefdef.height,  4,  4096);
			qglMatrixMode( GL_MODELVIEW );
		}
		GL_CullFace( GL_BACK );
	}
	else
	{
		if (!cullOnly) {
			qglMatrixMode( GL_PROJECTION );
			qglPopMatrix();
			qglMatrixMode( GL_MODELVIEW );
		}
		GL_CullFace( GL_FRONT );
	}
}

/*
=================
R_SetBlendModeOn
=================
*/
void R_SetBlendModeOn (image_t *skin)
{
	GL_TexEnv( GL_MODULATE );

	if (skin)
		GL_Bind(skin->texnum);

	GL_ShadeModel (GL_SMOOTH);

	if (currententity->flags & RF_TRANSLUCENT)
	{
		GL_DepthMask (false);

		if (currententity->flags & RF_TRANS_ADDITIVE)
		{ 
			GL_BlendFunc   (GL_SRC_ALPHA, GL_ONE);	
			qglColor4ub(255, 255, 255, 255);
			GL_ShadeModel (GL_FLAT);
		}
		else
			GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
		GL_Enable (GL_BLEND);
	}
}

/*
=================
R_SetBlendModeOff
=================
*/
void R_SetBlendModeOff (void)
{
	if ( currententity->flags & RF_TRANSLUCENT )
	{
		GL_DepthMask (true);
		GL_Disable(GL_BLEND);
		GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
}

/*
=================
R_SetShadeLight
=================
*/
void R_SetShadeLight (void)
{
	int		i;

	if (currententity->flags & RF_MASK_SHELL)
	{
		VectorClear (shadelight);
		if (currententity->flags & RF_SHELL_HALF_DAM)
		{
			shadelight[0] = 0.56;
			shadelight[1] = 0.59;
			shadelight[2] = 0.45;
		}
		if ( currententity->flags & RF_SHELL_DOUBLE )
		{
			shadelight[0] = 0.9;
			shadelight[1] = 0.7;
		}
		if ( currententity->flags & RF_SHELL_RED )
			shadelight[0] = 1.0;
		if ( currententity->flags & RF_SHELL_GREEN )
			shadelight[1] = 1.0;
		if ( currententity->flags & RF_SHELL_BLUE )
			shadelight[2] = 1.0;
	}
	else if ( currententity->flags & RF_FULLBRIGHT )
	{
		for (i=0 ; i<3 ; i++)
			shadelight[i] = 1.0;
	}
	else
	{
		// Set up basic lighting...
		if (r_model_shading->value && r_model_dlights->value)
		{
			int max = r_model_dlights->value;

			if (max<0) max=0;
			if (max>MAX_MODEL_DLIGHTS) max=MAX_MODEL_DLIGHTS;

			R_LightPointDynamics (currententity->origin, shadelight, model_dlights, 
				&model_dlights_num, max);
		}
		else
		{
			R_LightPoint (currententity->origin, shadelight, false);//true);
			model_dlights_num = 0;
		}

		// player lighting hack for communication back to server
		// big hack!
		if ( currententity->flags & RF_WEAPONMODEL )
		{
			// pick the greatest component, which should be the same
			// as the mono value returned by software
			if (shadelight[0] > shadelight[1])
			{
				if (shadelight[0] > shadelight[2])
					r_lightlevel->value = 150*shadelight[0];
				else
					r_lightlevel->value = 150*shadelight[2];
			}
			else
			{
				if (shadelight[1] > shadelight[2])
					r_lightlevel->value = 150*shadelight[1];
				else
					r_lightlevel->value = 150*shadelight[2];
			}

		}
		
		if ( r_monolightmap->string[0] != '0' )
		{
			float s = shadelight[0];

			if ( s < shadelight[1] )
				s = shadelight[1];
			if ( s < shadelight[2] )
				s = shadelight[2];

			shadelight[0] = s;
			shadelight[1] = s;
			shadelight[2] = s;
		}
	}

	if ( currententity->flags & RF_MINLIGHT )
	{
		for (i=0; i<3; i++)
			if (shadelight[i] > 0.02)
				break;
		if (i == 3)
		{
			shadelight[0] = 0.02;
			shadelight[1] = 0.02;
			shadelight[2] = 0.02;
		}
	}

	if ( currententity->flags & RF_GLOW )
	{	// bonus items will pulse with time
		float	scale;
		float	min;

		scale = 0.2 * sin(r_newrefdef.time*7);
		for (i=0 ; i<3 ; i++)
		{
			min = shadelight[i] * 0.8;
			shadelight[i] += scale;
			if (shadelight[i] < min)
				shadelight[i] = min;
		}
	}

	if (r_newrefdef.rdflags & RDF_IRGOGGLES)
	{
		if (currententity->flags & RF_IR_VISIBLE)
		{
			shadelight[0] = 1.0;
			shadelight[1] = 0.0;
			shadelight[2] = 0.0;
		}
	}
/*	if (r_newrefdef.rdflags & RDF_UVGOGGLES)
	{
		if (currententity->flags & RF_IR_VISIBLE)
		{
			shadelight[0] = 0.5;
			shadelight[1] = 1.0;
			shadelight[2] = 0.5;
		}
		else
		{
			shadelight[0] = 0.0;
			shadelight[1] = 1.0;
			shadelight[2] = 0.0;
		}
	}*/

	shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
}

/*
=================
R_DrawAliasModelBBox
=================
*/
void R_DrawAliasModelBBox (vec3_t bbox[8], entity_t *e, float red, float green, float blue, float alpha)
{
	int	i;

	if (!r_showbbox->value)
		return;

	if (e->flags & RF_WEAPONMODEL || e->flags & RF_VIEWERMODEL || e->flags & RF_BEAM || e->renderfx & RF2_CAMERAMODEL)
		return;

	GL_Disable (GL_CULL_FACE);
	qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
	qglDisableClientState (GL_COLOR_ARRAY);
	qglColor4f(red, green, blue, alpha);
	GL_DisableTexture (0);

	rb_vertex = rb_index = 0;
	indexArray[rb_index++] = rb_vertex+0;
	indexArray[rb_index++] = rb_vertex+1;
	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+1;
	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+3;

	indexArray[rb_index++] = rb_vertex+0;
	indexArray[rb_index++] = rb_vertex+1;
	indexArray[rb_index++] = rb_vertex+4;
	indexArray[rb_index++] = rb_vertex+1;
	indexArray[rb_index++] = rb_vertex+4;
	indexArray[rb_index++] = rb_vertex+5;

	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+4;
	indexArray[rb_index++] = rb_vertex+6;

	indexArray[rb_index++] = rb_vertex+1;
	indexArray[rb_index++] = rb_vertex+5;
	indexArray[rb_index++] = rb_vertex+7;

	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+3;
	indexArray[rb_index++] = rb_vertex+7;
	indexArray[rb_index++] = rb_vertex+2;
	indexArray[rb_index++] = rb_vertex+6;
	indexArray[rb_index++] = rb_vertex+7;

	indexArray[rb_index++] = rb_vertex+4;
	indexArray[rb_index++] = rb_vertex+5;
	indexArray[rb_index++] = rb_vertex+7;
	indexArray[rb_index++] = rb_vertex+4;
	indexArray[rb_index++] = rb_vertex+6;
	indexArray[rb_index++] = rb_vertex+7;
	for (i=0; i<8; i++) {
		VA_SetElem3v(vertexArray[rb_vertex], bbox[i]);
		rb_vertex++;
	}
	RB_DrawArrays ();
	rb_vertex = rb_index = 0;

	GL_EnableTexture (0);
	qglColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	qglEnableClientState (GL_COLOR_ARRAY);
	qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	GL_Enable (GL_CULL_FACE);
}
