#include "quakedef.h"

/*
===============================
			BUMPMAP
===============================
*/

/*
================
R_DrawSequentialBumpmapPoly. KH
================
*/

void R_DrawSequentialBumpmapPoly (msurface_t *s)
{
	glpoly_t	*p;
	float		*v;
	int			i;
	vec3_t		nv;
    int         detailscale;
    //
	// normal lightmaped poly
	//

    //if ((s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER|SURF_DRAWTILED)))
	//	return;
    if (s->detail == 0)
	    return;
    if ((s->shiny <= SHINYMETALL) && !(s->shiny == NOTSHINY))
	    return;
    if (s->caustic == 1)
        return;

    GL_Bind( detailtexture); 
    
    detailscale = min(max(1,gl_detailscale.value),64);    

	
    p = s->polys;
	
	//t = R_TextureAnimation (s->texinfo->texture);

	glBegin (GL_POLYGON);
	v = p->verts[0];

	if (gl_waterwarp.value != 0 && (r_viewleaf->contents == CONTENTS_WATER || r_viewleaf->contents == CONTENTS_LAVA || r_viewleaf->contents == CONTENTS_SLIME))
	{
		for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
        {
			glTexCoord2f (v[3]*detailscale, v[4]*detailscale);        
			nv[0] = v[0] + gl_waterwarp.value*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
		    nv[1] = v[1] + gl_waterwarp.value*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
		    nv[2] = v[2];
		    glVertex3fv (nv);
    	}	
	}
	else
	{
		for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
        {
			glTexCoord2f (v[3]*detailscale, v[4]*detailscale);
			glVertex3fv (v);
    	}	
	}	
	
	glEnd ();
}

/*
================
R_DrawSequentialCausticPoly. KH
================
*/
void R_DrawSequentialCausticPoly (msurface_t *s)
{
	glpoly_t	*p;
	int			tn;
    float       causticalpha = max(0.1f,(float) gl_causticalpha.value);	

    //if ((s->flags & (SURF_DRAWSKY|SURF_DRAWTURB)))
	//	return;
    //if (!(s->flags & SURF_UNDERWATER))
    //    return;
    
    //if (s->detail == 1)
	//    return;
    if (s->caustic == 0)
        return;
    if (num_caustics == 0)
        return;
	
    if (num_caustics != 0)
        {
        tn = (int)(host_time * (20*num_caustics/20))%(num_caustics);
	    GL_Bind( caustictexture[tn] );
	    }

    p = s->polys;
	
	//t = R_TextureAnimation (s->texinfo->texture);
	
	glBegin (GL_TRIANGLE_FAN);
	ProceedGLPolyVertex(p);
	glEnd ();
}

/*
================
R_DrawSequentialChromePoly. KH
================
*/

extern float	r_world_matrix[16];

void R_DrawSequentialChromePoly (msurface_t *s)
{
	glpoly_t	*p;
	float		*v;
	int			i;
	vec3_t		nv, nv2;

	vec3_t		playerorg;
	//jeank
	vec3_t		viewer, reflected, normal;
	float		d;
	float		drawdistance;
	//jeank
    float       shinyalpha;// = max(0.1f,(float) gl_shinyalpha.value);
	
    //if (s->flags & (SURF_DRAWSKY|SURF_UNDERWATER))
    //    return;
    //if (s->detail == 1)
	//    return;
    if (s->caustic == 1)
        return;
    if (s->shiny == NOTSHINY)
		return;    

	//jeank
	normal[0]=s->plane->normal[0];
	normal[1]=s->plane->normal[1];
	normal[2]=s->plane->normal[2];
	//Modify player org for movable entities
	VectorCopy(r_refdef.vieworg, playerorg); 
	VectorSubtract (playerorg, currententity->origin, playerorg);
	//Modify player org for rotating entities
	if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2])  // Если бмодель вращается...
	{
		vec3_t	temp;
		vec3_t	forward, right, up;

		AngleVectors (currententity->angles, forward, right, up);
		VectorCopy (playerorg, temp);
		playerorg[0] = DotProduct (temp, forward);
		playerorg[1] = -DotProduct (temp, right);
		playerorg[2] = DotProduct (temp, up);
	}
	//jeank

    if ((s->shiny >= SHINYGLASS) && (s->shiny <= SHINYGLASSCRACKED2))
	{
		shinyalpha = max(0.1f,(float) gl_shinyglassalpha.value);	
		GL_Bind(chrometexture);
		drawdistance = gl_shinyglassscale.value;
	}
	else if (s->shiny == SHINYMETALL)
	{
		shinyalpha = max(0.1f,(float) gl_shinymetalalpha.value);	
		if (chrometexture2)
			GL_Bind(chrometexture2);
		else
			if (chrometexture3)
				GL_Bind(chrometexture3);
			else
				return;
		drawdistance =  gl_shinymetalscale.value;
	}
	else if (s->shiny == SHINYMETALLRUSTED || s->shiny == SHINYMETALLRUSTED2)
	{
		shinyalpha = max(0.1f,(float) gl_shinymetalrustedalpha.value);	
		if (chrometexture2)
			GL_Bind(chrometexture2);
		else
			if (chrometexture3)
				GL_Bind(chrometexture3);
			else
				return;
		drawdistance = gl_shinymetalrustedscale.value;
	}
	else if (s->shiny == SHINYSTONECRACKED || s->shiny == SHINYSTONECRACKED2)
	{
		shinyalpha = max(0.1f,(float) gl_shinycrackedalpha.value);	
		if (chrometexture3)
			GL_Bind(chrometexture3);
		else
			if (chrometexture2)
				GL_Bind(chrometexture2);
			else
				return;
		drawdistance = gl_shinycrackedscale.value;
	}
    else
	{
		shinyalpha = max(0.1f,(float) gl_shinyalpha.value);	
		if (chrometexture3)
			GL_Bind(chrometexture3);
		else
			if (chrometexture2)
				GL_Bind(chrometexture2);
			else
				return;		
		drawdistance = gl_shinyscale.value;
	}

	glColor4f(shinyalpha, shinyalpha, shinyalpha, shinyalpha);

    p = s->polys;
	
    glBegin (GL_POLYGON);
	
    v = p->verts[0];

	if (gl_waterwarp.value != 0 && (r_viewleaf->contents == CONTENTS_WATER || r_viewleaf->contents == CONTENTS_LAVA || r_viewleaf->contents == CONTENTS_SLIME))
	{
		for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
        {
		    nv[0] = v[0] + gl_waterwarp.value*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
		    nv[1] = v[1] + gl_waterwarp.value*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
		    nv[2] = v[2];
			
			VectorSubtract (playerorg, v, viewer);
			VectorNormalize (viewer);
			d = DotProduct (normal, viewer);

			reflected[0] = normal[0]*2*d - viewer[0]*0.2;
			reflected[1] = normal[1]*2*d - viewer[1]*0.2;
			reflected[2] = normal[2]*2*d - viewer[2]*0.2;
			
			if (gl_shinynormalize.value)
				VectorNormalize (reflected);
			nv2[0] = ((1-gl_shinydistancefactor.value) + gl_shinydistancefactor.value*(reflected[1]*normal[2] + reflected[0]*normal[1] + reflected[2]*normal[0]))*drawdistance;
			nv2[1] = ((1-gl_shinydistancefactor.value) - gl_shinydistancefactor.value*(reflected[0]*abs(normal[2]) + reflected[2]*abs(normal[1]) + reflected[1]*abs(normal[0])))*drawdistance;			
			
			glTexCoord2f (nv2[0], nv2[1]);
			glVertex3fv (nv);

    	}	
	}
	else
	{
		for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
        {
			VectorSubtract (playerorg, v, viewer);
			VectorNormalize (viewer);
			d = DotProduct (normal, viewer);
			
			reflected[0] = normal[0]*2*d - viewer[0]*0.2;
			reflected[1] = normal[1]*2*d - viewer[1]*0.2;
			reflected[2] = normal[2]*2*d - viewer[2]*0.2;
			
			if (gl_shinynormalize.value)
				VectorNormalize (reflected);
			nv2[0] = ((1-gl_shinydistancefactor.value) + gl_shinydistancefactor.value*(reflected[1]*normal[2] + reflected[0]*normal[1] + reflected[2]*normal[0]))*drawdistance;
			nv2[1] = ((1-gl_shinydistancefactor.value) - gl_shinydistancefactor.value*(reflected[0]*abs(normal[2]) + reflected[2]*abs(normal[1]) + reflected[1]*abs(normal[0])))*drawdistance;			
			
			//Quake3 method
			//nv2[0] = (0.5 + reflected[1] * 0.5)*drawdistance;
			//nv2[1] = (0.5 - reflected[2] * 0.5)*drawdistance;
			
			glTexCoord2f (nv2[0], nv2[1]);
			glVertex3fv (v);
    	}	
	}
    glEnd ();
}

/*
================
R_DrawSequentialRustyPoly
================
*/
void R_DrawSequentialRustyPoly (msurface_t *s)
{
	glpoly_t	*p;
	float		*v;
	int			i;
	vec3_t		nv;
    float		detailscale;

    if ((s->shiny == NOTSHINY) || (s->shiny == SHINYGLASS) || (s->shiny == SHINYMETALL) || (s->shiny == SHINYSTONE))
	    return;

	switch (s->shiny)
	{
		case SHINYGLASSCRACKED:
			if (!scratchglasstexture)
				return;
			GL_Bind(scratchglasstexture);
			detailscale = min(max(0.1, gl_rustyglassscale.value),16);
		break;
		case SHINYGLASSCRACKED2:
			if (!scratchglasstexture2)
				return;
			GL_Bind(scratchglasstexture2);
			detailscale = min(max(0.1, gl_rustyglassscale.value),16);
		break;
		case SHINYMETALLRUSTED:
			if (!rustmetaltexture)
			{
				if (!rustmetaltexture2)
					return;
				else
					GL_Bind(rustmetaltexture2);
			}
			else
				GL_Bind(rustmetaltexture);
			detailscale = min(max(0.1, gl_rustymetalscale.value),16);
		break;
		case SHINYMETALLRUSTED2:
			if (!rustmetaltexture2)
			{
				if (!rustmetaltexture)
					return;
				else
					GL_Bind(rustmetaltexture);
			}
			else
				GL_Bind(rustmetaltexture2);
			detailscale = min(max(0.1, gl_rustymetalscale.value),16);
		break;
		case SHINYSTONECRACKED:
			if (!crackstonetexture)
			{
				if (!crackstonetexture2)
					return;
				else
					GL_Bind(crackstonetexture2);
			}
			else
				GL_Bind(crackstonetexture);
			detailscale = min(max(0.1, gl_rustyscale.value),16);
		break;
		case SHINYSTONECRACKED2:
			if (!crackstonetexture2)
			{
				if (!crackstonetexture)
					return;
				else
					GL_Bind(crackstonetexture);
			}
			else
				GL_Bind(crackstonetexture2);
			detailscale = min(max(0.1, gl_rustyscale.value),16);
		break;
		case SHINYSTONECRACKED3:
			if (!crackstonetexture3)
			{
				if (!crackstonetexture2)
				{
					if (!crackstonetexture)
						return;
					else
					GL_Bind(crackstonetexture);
				}
				else
					GL_Bind(crackstonetexture2);
			}
			else
				GL_Bind(crackstonetexture3);
			detailscale = min(max(0.1, gl_rustyscale.value),16);
		break;
	}

    p = s->polys;
	glBegin (GL_TRIANGLE_FAN);
	//ProceedGLPolyVertex(p);
	v = p->verts[0];

	if (gl_waterwarp.value != 0 && (r_viewleaf->contents == CONTENTS_WATER || r_viewleaf->contents == CONTENTS_LAVA || r_viewleaf->contents == CONTENTS_SLIME))
	{
		for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
        {
			glTexCoord2f (v[3]*detailscale, v[4]*detailscale);        
			nv[0] = v[0] + gl_waterwarp.value*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
		    nv[1] = v[1] + gl_waterwarp.value*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
		    nv[2] = v[2];
		    glVertex3fv (nv);
    	}	
	}
	else
	{
		for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
        {
			glTexCoord2f (v[3]*detailscale, v[4]*detailscale);
			glVertex3fv (v);
    	}	
	}
	glEnd ();
}

/*
=======================
R_DrawBumpmapBrushModel. KH
=======================
*/
void R_DrawBumpmapBrushModel (entity_t *e, int type)
{
	vec3_t		mins, maxs;
	int			i;
	msurface_t	*psurf;
	float		dot;
	mplane_t	*pplane;
	model_t		*clmodel;
	qboolean	rotated;

	currententity = e;
	currenttexture = -1;

	clmodel = e->model;

	if (e->angles[0] || e->angles[1] || e->angles[2])
	{
		rotated = true;
		for (i=0 ; i<3 ; i++)
		{
			mins[i] = e->origin[i] - clmodel->radius;
			maxs[i] = e->origin[i] + clmodel->radius;
		}
	}
	else
	{
		rotated = false;
		VectorAdd (e->origin, clmodel->mins, mins);
		VectorAdd (e->origin, clmodel->maxs, maxs);
	}

	if (R_CullBox (mins, maxs))
		return;


	VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
	if (rotated)
	{
		vec3_t	temp;
		vec3_t	forward, right, up;

		VectorCopy (modelorg, temp);
		AngleVectors (e->angles, forward, right, up);
		modelorg[0] = DotProduct (temp, forward);
		modelorg[1] = -DotProduct (temp, right);
		modelorg[2] = DotProduct (temp, up);
	}

	psurf = &clmodel->surfaces[clmodel->firstmodelsurface];

    glPushMatrix ();
    e->angles[0] = -e->angles[0];	// stupid quake bug
    e->angles[2] = -e->angles[2];	// stupid quake bug
	R_RotateForEntity (e);
    e->angles[0] = -e->angles[0];	// stupid quake bug
    e->angles[2] = -e->angles[2];	// stupid quake bug
	//
	// draw texture
	//
	for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++)
	{
	// find which side of the node we are on
		pplane = psurf->plane;

		dot = DotProduct (modelorg, pplane->normal) - pplane->dist;

    	// draw the polygon
		if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
			(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
		{                
   			if (gl_detailtexture.value == 1 && type == DF_DETAIL)
                R_DrawSequentialBumpmapPoly (psurf);

			if (gl_caustictexture.value == 1 && type == DF_CAUSTIC)
                R_DrawSequentialCausticPoly (psurf);

			if (gl_shinytexture.value == 1 && type == DF_SHINY)
                R_DrawSequentialChromePoly (psurf);

			if (gl_rustytexture.value == 1 && type == DF_RUSTY)
				R_DrawSequentialRustyPoly (psurf);
		}
	}

	glPopMatrix ();
}

/*
=============
R_DrawBumpmapEntitiesOnList. KH
=============
*/
void R_DrawBumpmapEntitiesOnList (int type)
{
	int		i;

	if (!r_drawentities.value)
		return;

	for (i=0 ; i<cl_numvisedicts ; i++) {
		currententity = cl_visedicts[i];

		switch (currententity->model->type) {
		case mod_brush:
			if (((currententity->drawflags & DRF_TRANSLUCENT)) == 0)
			{
				if (gl_ztrick.value)
				{
					R_DrawBumpmapBrushModel (currententity, type);
				}
				else
				{
					glPolygonOffset(0.0559, 25);
					glEnable (GL_POLYGON_OFFSET_FILL); 
					glEnable (GL_POLYGON_OFFSET_LINE);
					R_DrawBumpmapBrushModel (currententity, type);
					glPolygonOffset(0, 0);
					glDisable (GL_POLYGON_OFFSET_FILL);
					glDisable (GL_POLYGON_OFFSET_LINE);					
				}
			}
			break;
		}
	}
}

//
// Extra bumpmap pass. KH
//
void R_ExtraBumpPass (void)
{

    // disable multitexturing - just in case ... 
    /*
    if (gl_multitexture.value == 1 && gl_mtexable == true)
        {
        SelectTextureMTex (GL_MTexture1);
        glDisable(GL_TEXTURE_2D);
        SelectTextureMTex (GL_MTexture0);
        }
    */


    if (gl_detailtexture.value == 1 || gl_caustictexture.value == 1 || gl_shinytexture.value == 1)
	{
		float detailalpha = (float) gl_detailalpha.value;
		float causticalpha = (float) gl_causticalpha.value;
		float shinyalpha = max(0.1f,(float) gl_shinyalpha.value);	

		//GL_Bind(detailtexture);

		if (gl_lightmap_format == GL_RGBA) 
		{
			glEnable (GL_BLEND);
			glColor4f(detailalpha, detailalpha, detailalpha, detailalpha);
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0.5);
                
			R_DrawBumpmapEntitiesOnList(DF_DETAIL);

			glColor4f (causticalpha, causticalpha, causticalpha, causticalpha);
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

			R_DrawBumpmapEntitiesOnList(DF_CAUSTIC);

			glColor4f (shinyalpha,shinyalpha,shinyalpha,shinyalpha);
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0.5);

			R_DrawBumpmapEntitiesOnList(DF_SHINY);

			
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			R_DrawBumpmapEntitiesOnList(DF_RUSTY);

			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glColor4f(1.0f,1.0f,1.0f,1.0f);
			glDisable (GL_BLEND);
		}
		else
		{
			glEnable (GL_BLEND);
			//glColor4f(1, 1, 1, detailalpha);
			glColor4f(detailalpha, detailalpha, detailalpha, detailalpha);
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

			R_DrawBumpmapEntitiesOnList(DF_DETAIL);

			glColor4f (causticalpha, causticalpha, causticalpha, causticalpha);
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

			R_DrawBumpmapEntitiesOnList(DF_CAUSTIC);

			glColor4f (shinyalpha,shinyalpha,shinyalpha,shinyalpha);
			glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

			R_DrawBumpmapEntitiesOnList(DF_SHINY);

			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			R_DrawBumpmapEntitiesOnList(DF_RUSTY);

			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
			glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glColor4f(1.0f,1.0f,1.0f,1.0f);
			glDisable (GL_BLEND);
		}
	}
}


/*
================
R_RecursiveBumpmapWorldNode. KH
================
*/
void R_RecursiveBumpmapWorldNode (mnode_t *node, int type)
{
	int			c, side;
	mplane_t	*plane;
	msurface_t	*surf, **mark;
	mleaf_t		*pleaf;
	double		dot;

	if (node->contents == CONTENTS_SOLID)
		return;		// solid

	if (node->visframe != r_visframecount)
		return;

	if (R_CullBox (node->minmaxs, node->minmaxs+3))
		return;
	
// if a leaf node, draw stuff
	if (node->contents < 0)
	{
		pleaf = (mleaf_t *)node;

		mark = pleaf->firstmarksurface;
		c = pleaf->nummarksurfaces;

		if (c)
		{
			do
			    {
			    (*mark)->visframe = r_framecount;
				mark++;
			    } 
            while (--c);
		}

	// deal with model fragments in this leaf
		if (pleaf->efrags)
			R_StoreEfrags (&pleaf->efrags);

		return;
	}

// node is just a decision point, so go down the apropriate sides

// find which side of the node we are on
	plane = node->plane;

	switch (plane->type)
	{
	case PLANE_X:
		dot = modelorg[0] - plane->dist;
		break;
	case PLANE_Y:
		dot = modelorg[1] - plane->dist;
		break;
	case PLANE_Z:
		dot = modelorg[2] - plane->dist;
		break;
	default:
		dot = DotProduct (modelorg, plane->normal) - plane->dist;
		break;
	}

	if (dot >= 0)
		side = 0;
	else
		side = 1;

// recurse down the children, front side first
	R_RecursiveBumpmapWorldNode (node->children[side], type);

// draw stuff
	c = node->numsurfaces;

	if (c)
	    {
		surf = cl.worldmodel->surfaces + node->firstsurface;

		if (dot < 0 -BACKFACE_EPSILON)
			side = SURF_PLANEBACK;
		else if (dot > BACKFACE_EPSILON)
			side = 0;
		    {
			for ( ; c ; c--, surf++)
			    {
				if (surf->visframe != r_framecount)
					continue;

				// don't backface underwater surfaces, because they warp
				if ( !(surf->flags & SURF_UNDERWATER) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) )
					continue;		// wrong side

				// if sorting by texture, just store it out
				if (gl_texsort.value)
				    {
                    /*
					if (!mirror
					|| surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum])
						{
						surf->texturechain = surf->texinfo->texture->texturechain;
						surf->texinfo->texture->texturechain = surf;
						}
						*/
                    // Skip texsort.
					
                    if (gl_detailtexture.value == 1 && type == DF_DETAIL)
                        R_DrawSequentialBumpmapPoly (surf);
                    
                    if (gl_caustictexture.value == 1 && type == DF_CAUSTIC)
                        R_DrawSequentialCausticPoly (surf);

                    if (gl_shinytexture.value == 1 && type == DF_SHINY)
                        R_DrawSequentialChromePoly (surf);

					if (gl_rustytexture.value == 1 && type == DF_RUSTY)
						R_DrawSequentialRustyPoly (surf);

                    continue;
                    }
                 else if (surf->flags & SURF_DRAWSKY) 
                    { 
					// Skip sky.
					continue;
				    } 
                 else if (surf->flags & SURF_DRAWTURB)
                    { 
					// Skip turbulance.
					continue; 
				    } 
                 else
                    {
   					if (gl_detailtexture.value == 1 && type == DF_DETAIL)
                        R_DrawSequentialBumpmapPoly (surf);

                    if (gl_caustictexture.value == 1 && type == DF_CAUSTIC)
                        R_DrawSequentialCausticPoly (surf);

                    if (gl_shinytexture.value == 1 && type == DF_SHINY)
                        R_DrawSequentialChromePoly (surf);

					if (gl_rustytexture.value == 1 && type == DF_RUSTY)
						R_DrawSequentialRustyPoly (surf);
					}
			    }
		    }

	}

// recurse down the back side
	R_RecursiveBumpmapWorldNode (node->children[!side], type);
}

void R_ExtraBumpPassRecursive (void)
{
    // disable multitexturing - just in case ... 
        
    if (gl_multitexture.value == 1 && gl_mtexable == true)
        {
        SelectTextureMTex (GL_MTexture1);
        glDisable(GL_TEXTURE_2D);
        SelectTextureMTex (GL_MTexture0);
        glEnable(GL_TEXTURE_2D);
        }

    //
	// Extra bumpmap pass. KH
	//
    if (gl_detailtexture.value == 1 || gl_caustictexture.value == 1 || gl_shinytexture.value == 1)
        {
	     	float detailalpha = (float) gl_detailalpha.value;
		    float causticalpha = (float) gl_causticalpha.value;
            float shinyalpha = max(0.1f,(float) gl_shinyalpha.value);	
    
            if (gl_lightmap_format == GL_RGBA) 
                {
                glEnable (GL_BLEND);
		        glColor4f(detailalpha, detailalpha, detailalpha, detailalpha);
                glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
     	        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0.5);
                                                
                R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_DETAIL);

                glColor4f (causticalpha, causticalpha, causticalpha, causticalpha);
                glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
	            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                    
                R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_CAUSTIC);

                glColor4f (shinyalpha,shinyalpha,shinyalpha,shinyalpha);
	            glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
     	        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0.5);
                
                R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_SHINY);

                glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

				R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_RUSTY);
		        
				glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	            glColor4f(1.0f,1.0f,1.0f,1.0f);
		        glDisable (GL_BLEND);
                }
            else {
                glEnable (GL_BLEND);
		        //glColor4f(1.0f, 1.0f, 1.0f, detailalpha);
                glColor4f(detailalpha, detailalpha, detailalpha, detailalpha);
                glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
     	        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

                R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_DETAIL);

                glColor4f (causticalpha, causticalpha, causticalpha, causticalpha);
                glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
		        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                
                R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_CAUSTIC);

                glColor4f (shinyalpha,shinyalpha,shinyalpha,shinyalpha);
                glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
     	        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
            
                R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_SHINY);

                glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

				R_RecursiveBumpmapWorldNode (cl.worldmodel->nodes, DF_RUSTY);
		    
				glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	            glColor4f(1.0f,1.0f,1.0f,1.0f);
		        glDisable (GL_BLEND);
                }
		}
}

void EmitWaterPolysReflection (msurface_t *fa)
{
	glpoly_t	*p;
	vec3_t		nv, nv2;
	float		*v;
	int			i;
	float		os, ot;
    
	//jeank
	vec3_t		viewer, reflected, normal;
	float		d;
	//jeank	
    
    //jkrige - clamp waterripple values
	if(gl_waterripple.value > 10)
		gl_waterripple.value = 10;

	if(gl_waterripple.value < 0)
		gl_waterripple.value=0;
	//jkrige - clamp waterripple values

	//jeank
	normal[0]=fa->plane->normal[0];
	normal[1]=fa->plane->normal[1];
	normal[2]=fa->plane->normal[2];
	//jeank

    if (waterreflecttexture)
		GL_Bind(waterreflecttexture);
	else
		return;

    if (r_wateralpha.value == 1)
		glEnable (GL_BLEND);

	glColor4f (1.0f,1.0f,1.0f,r_wateralpha.value);
       
	//glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 
	glBlendFunc(GL_DST_COLOR, GL_ONE); 
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0.5);
        
	for (p = fa->polys ; p ; p = p->next)
	{
		glBegin (GL_POLYGON);

		if (gl_waterwarp.value != 0 && (r_viewleaf->contents == CONTENTS_WATER || r_viewleaf->contents == CONTENTS_LAVA || r_viewleaf->contents == CONTENTS_SLIME))
		{
			for (i=0,v = p->verts[0] ; i < p->numverts ; i++, v += VERTEXSIZE)
			{
				os = v[3];
				ot = v[4];

				//jkrige - waterripple
				nv[0] = v[0] + gl_waterwarp.value*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
				nv[1] = v[1] + gl_waterwarp.value*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
				nv[2] = v[2] + gl_waterripple.value*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
				//jkrige - waterripple
			
				VectorSubtract (r_refdef.vieworg, v, viewer); //r_refdef.vieworg //r_origin
				VectorNormalize (viewer); //glNormal3fv(viewer);
				d = DotProduct (normal, viewer);
				
				reflected[0] = normal[0]*2*d - viewer[0]*0.2;
				reflected[1] = normal[1]*2*d - viewer[1]*0.2;
				reflected[2] = normal[2]*2*d - viewer[2]*0.2;
				
				nv2[0] = (0.5 + reflected[1] * 0.5)*2*(1+sin(realtime)*0.05)+realtime*0.01;
				nv2[1] = (0.5 - reflected[2] * 0.5)*2*(1+cos(realtime)*0.05)+realtime*0.01;
				
				glTexCoord2f (nv2[0], nv2[1]);
				glVertex3fv (nv);
			}
		}
		else
		{
			for (i=0,v = p->verts[0] ; i < p->numverts ; i++, v += VERTEXSIZE)
			{
				os = v[3];
				ot = v[4];

				//jkrige - waterripple
				nv[0] = v[0];
				nv[1] = v[1];
				nv[2] = v[2] + gl_waterripple.value*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
				//jkrige - waterripple
			
				VectorSubtract (r_refdef.vieworg, v, viewer); //r_refdef.vieworg //r_origin
				VectorNormalize (viewer); //glNormal3fv(viewer);
				d = DotProduct (normal, viewer);

				reflected[0] = normal[0]*2*d - viewer[0]*0.2;
				reflected[1] = normal[1]*2*d - viewer[1]*0.2;
				reflected[2] = normal[2]*2*d - viewer[2]*0.2;

				nv2[0] = (0.5 + reflected[1] * 0.5)*2*(1+sin(realtime)*0.05)+realtime*0.01;
				nv2[1] = (0.5 - reflected[2] * 0.5)*2*(1+cos(realtime)*0.05)+realtime*0.01;

				glTexCoord2f (nv2[0], nv2[1]);
				glVertex3fv (nv);
			}
		}
		glEnd ();
	}

    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   
    if (r_wateralpha.value == 1)
        glDisable(GL_BLEND);
}