/*
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_particle.c -- particle rendering
// moved from r_main.c

#include "r_local.h"

#define random()	((rand () & 0x7fff) / ((float)0x7fff))
#define crandom()	(2.0 * (random() - 0.5))

//#define BINARY_PART_SORT


#ifdef BINARY_PART_SORT
/*
===================================================
BINARY PARTICLE SORT
===================================================
*/
int partstosort;
sortedelement_t theparts[MAX_PARTICLES];
sortedelement_t *parts_prerender;
// Is this really needed?
//sortedelement_t *parts_last;

void renderParticle (particle_t *p);
void renderDecal (particle_t *p);

void resetPartSortList (void)
{
	partstosort = 0;
	parts_prerender = NULL;
	//parts_last = NULL;
}

qboolean particleClip( float len )
{
	if (gl_particle_min->value > 0)
	{
		if (len < gl_particle_min->value)
			return true;
	}
	if (gl_particle_max->value > 0)
	{
		if (len > gl_particle_max->value)
			return true;
	}

	return false;
}

sortedelement_t *NewSortPart ( particle_t *p)
{
	vec3_t distance;
	sortedelement_t *element;

	element = &theparts[partstosort];

	VectorSubtract(p->origin, r_origin, distance);
	VectorCopy(p->origin, element->org);

	element->data	= p;
	element->len	= VectorLength(distance);
	element->left	= NULL;
	element->right	= NULL;

	return element;
}

void AddPartTransTree( particle_t *p )
{
	sortedelement_t *thisPart;

	thisPart = NewSortPart(p);

	if (particleClip(thisPart->len))
		return;
	
	if (parts_prerender)
		ElementAddNode(parts_prerender, thisPart);	
	else
		parts_prerender = thisPart;
	
	//parts_last = thisPart;
	partstosort++;
}

void R_BuildParticleList (void)
{
	int		i;
	resetPartSortList();

	for ( i=0; i < r_newrefdef.num_particles; i++)
		AddPartTransTree(&r_newrefdef.particles[i]);
}

void RenderParticleTree (sortedelement_t *element)
{
	if (!element)
		return;

	RenderParticleTree(element->left);

	if (element->data)
		renderParticle((particle_t *)element->data);

	RenderParticleTree(element->right);
}
#endif // BINARY_PART_SORT


/*
===================================================

STANDARD PARTICLE SORT

===================================================
*/

// not used anymore
/*typedef struct sortedent_s 
{
	entity_t *ent;
	vec_t len;
} sortedent_t;
sortedent_t theoldents[MAX_ENTITIES];*/

typedef struct sortedpart_s
{
	particle_t *p;
	vec_t len;
} sortedpart_t;

sortedpart_t theoldparts[MAX_PARTICLES];

int transCompare (const void *arg1, const void *arg2 ) 
{
	const sortedpart_t *a1, *a2; //was sortedent_t
	a1 = (sortedpart_t *) arg1;
	a2 = (sortedpart_t *) arg2;
	return (a2->len - a1->len);
}

sortedpart_t NewSortedPart (particle_t *p)
{
	vec3_t result;
	sortedpart_t s_part;

	s_part.p = p;
	VectorSubtract(p->origin, r_origin, result);
	s_part.len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]);

	return s_part;
}

void R_SortParticlesOnList (void)
{
	int i/*, j=0*/;

	for (i = 0; i < r_newrefdef.num_particles; i++)
		theoldparts[i] = NewSortedPart(&r_newrefdef.particles[i]);
	qsort((void *) theoldparts, r_newrefdef.num_particles, sizeof(sortedpart_t), transCompare);
}


/*
===================================================

PARTICLE RENDERING

===================================================
*/
int texParticle (int type)
{	
	// check for out of bounds image num
	type =  max( type, 0 );
	type =  min( type, PARTICLE_TYPES-1 );
	// check for bad particle image num
	if (!r_particletextures[type])
		r_particletextures[type] = r_notexture;
	return r_particletextures[type]->texnum;
}

void vectoanglerolled (vec3_t value1, float angleyaw, vec3_t angles);
/*void vectoanglerolled (vec3_t value1, float angleyaw, vec3_t angles)
{
	float	forward, yaw, pitch;

	yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
	forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
	pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);

	if (pitch < 0)
		pitch += 360;

	angles[PITCH] = -pitch;
	angles[YAW] =  yaw;
	angles[ROLL] = - angleyaw;
}*/

float AngleFind(float input)
{
	return 180.0/input;
}

void setBeamAngles (vec3_t start, vec3_t end, vec3_t up, vec3_t right)
{
	vec3_t move, delta;

	VectorSubtract(end, start, move);
	VectorNormalize(move);

	VectorCopy(move, up);
	VectorSubtract(r_newrefdef.vieworg, start, delta);
	CrossProduct(up, delta, right);
	//if(!VectorCompare(right, vec3_origin))
		VectorNormalize(right);
}

vec3_t particle_coord[4];


qboolean ParticleOverbright;
void SetParticleOverbright (qboolean toggle)
{
	if ( (toggle && !ParticleOverbright) || (!toggle && ParticleOverbright) )
	{
		SetVertexOverbrights(toggle);
		ParticleOverbright = toggle;
	}
}

void getParticleLight (particle_t *p, vec3_t pos, float lighting, vec3_t shadelight)
{
	int j;
	float lightest = 0;

	if (!lighting)
	{
		VectorSet(shadelight, p->red, p->green, p->blue);
		return;
	}

	R_LightPoint (pos, shadelight, false);

	shadelight[0]= (lighting*shadelight[0]+(1-lighting)) * p->red;
	shadelight[1]= (lighting*shadelight[1]+(1-lighting)) * p->green;
	shadelight[2]= (lighting*shadelight[2]+(1-lighting)) * p->blue;

	//this cleans up the lighting
	{
		for (j=0;j<3;j++)
			if (shadelight[j]>lightest)
				lightest= shadelight[j];
		if (lightest>255)
			for (j=0;j<3;j++)
			{
				shadelight[j]*= 255/lightest;
				if (shadelight[j]>255)
					shadelight[j] = 255;
			}

		for (j=0;j<3;j++)
		{
			if (shadelight[j]<0)
				shadelight[j] = 0;
		}	
	}
}

//id move this somewhere less dirty, but im too damn lazy - psychospaz
#define DEPTHHACK_RANGE_SHORT	0.9985f
#define DEPTHHACK_RANGE_MID		0.975f
#define DEPTHHACK_RANGE_LONG	0.95f

/*
===============
renderParticle
===============
*/
void renderParticle (particle_t *p)
{
	float	size, lighting = gl_particle_lighting->value;
//	int		oldrender=0, rendertype=0;
	
	vec3_t	up		= {vup[0]    * 0.75f, vup[1]    * 0.75f, vup[2]    * 0.75f};
	vec3_t	right	= {vright[0] * 0.75f, vright[1] * 0.75f, vright[2] * 0.75f};
	vec3_t	shadelight;
	qboolean	shaded;
	byte	alpha;
	vec3_t	coord[4];

	VectorCopy(particle_coord[0], coord[0]);
	VectorCopy(particle_coord[1], coord[1]);
	VectorCopy(particle_coord[2], coord[2]);
	VectorCopy(particle_coord[3], coord[3]);

	GL_BlendFunction (p->blendfunc_src, p->blendfunc_dst);
	
	alpha	= p->alpha * 255;
	size = (p->size>0.1) ? p->size : 0.1;

	if (!(p->flags&PART_SPARK))
		GL_Bind(texParticle(p->image));
	
	if (p->flags&PART_DEPTHHACK_SHORT) //nice little poly-peeking - psychospaz
		qglDepthRange (gldepthmin, gldepthmin + DEPTHHACK_RANGE_SHORT*(gldepthmax-gldepthmin));
	if (p->flags&PART_DEPTHHACK_MID)
		qglDepthRange (gldepthmin, gldepthmin + DEPTHHACK_RANGE_MID*(gldepthmax-gldepthmin));
	if (p->flags&PART_DEPTHHACK_LONG)
		qglDepthRange (gldepthmin, gldepthmin + DEPTHHACK_RANGE_LONG*(gldepthmax-gldepthmin));

	if (p->flags&PART_OVERBRIGHT)
		SetParticleOverbright(true);

	shaded = (p->flags&PART_SHADED);

	if (shaded)
	{
		getParticleLight (p, p->origin, lighting, shadelight);
		qglColor4ub((byte)shadelight[0], (byte)shadelight[1], (byte)shadelight[2], alpha);
	}
	else
		qglColor4ub((byte)p->red, (byte)p->green, (byte)p->blue, alpha);
	
	
	if (p->flags&PART_SPARK) //QMB style particles
	{
		int angle;
		vec3_t v;
		
		qglDisable (GL_TEXTURE_2D);
		
		qglPushMatrix();
		{
			qglBegin (GL_TRIANGLE_FAN);
			{
				qglVertex3fv (p->origin);
				qglColor4ub( 0, 0, 0, 0);
				
				for (angle=7*22.5 ; angle>=0 ; angle-=22.5)
				{
					v[0]=p->origin[0] - p->angle[0]*size + (right[0]*cos(angle) + up[0]*sin(angle));
					v[1]=p->origin[1] - p->angle[1]*size + (right[1]*cos(angle) + up[1]*sin(angle));
					v[2]=p->origin[2] - p->angle[2]*size + (right[2]*cos(angle) + up[2]*sin(angle));
					qglVertex3fv (v);
				}
			}
			qglEnd ();
		}
		qglPopMatrix ();
		
		qglEnable (GL_TEXTURE_2D);
	}
	else if (p->flags&PART_BEAM) //given start and end positions, will have fun here :)
	{	//p->angle = start
		//p->origin= end
		vec3_t	angl_coord[4];
		vec3_t	ang_up, ang_right;//, vdelta;
		
		setBeamAngles(p->angle, p->origin, ang_up, ang_right);
		VectorScale(ang_right, size, ang_right);
		
		VectorAdd(p->origin, ang_right, angl_coord[0]);
		VectorAdd(p->angle, ang_right, angl_coord[1]);
		VectorSubtract(p->angle, ang_right, angl_coord[2]);
		VectorSubtract(p->origin, ang_right, angl_coord[3]);
		
		qglPushMatrix();
		{
			qglBegin (GL_QUADS);
			{
				qglTexCoord2f (0, 1);
				qglVertex3fv  (angl_coord[0]);
				qglTexCoord2f (0, 0);
				qglVertex3fv  (angl_coord[1]);
				qglTexCoord2f (1, 0); 
				qglVertex3fv  (angl_coord[2]);
				qglTexCoord2f (1, 1);
				qglVertex3fv  (angl_coord[3]);
			}
			qglEnd ();
			
		}
		qglPopMatrix ();
	}
	else if (p->flags&PART_LIGHTNING) //given start and end positions, will have fun here :)
	{	//p->angle = start
		//p->origin= end
		float	tlen, halflen, len, dec = size, warpfactor, warpadd, oldwarpadd, warpsize, time, i=0, factor;//, j;
		vec3_t	move, lastvec, thisvec, tempvec;
		vec3_t	angl_coord[4];
		vec3_t	ang_up, ang_right, vdelta;
		
		warpsize = dec * 0.4; //Knightmare changed
		warpfactor = M_PI*1.0;
		time = -r_newrefdef.time*20.0;
		
		VectorSubtract(p->origin, p->angle, move);
		len = VectorNormalize(move);
		
		VectorCopy(move, ang_up);
		VectorSubtract(r_newrefdef.vieworg, p->angle, vdelta);
		CrossProduct(ang_up, vdelta, ang_right);
		if(!VectorCompare(ang_right, vec3_origin))
			VectorNormalize(ang_right);
		
		VectorScale (ang_right, dec, ang_right);
		VectorScale (ang_up, dec, ang_up);
		
		VectorScale(move, dec, move);
		VectorCopy(p->angle, lastvec);
		VectorAdd(lastvec, move, thisvec);
		VectorCopy(thisvec, tempvec);
		
		//lightning starts at point and then flares out
		#define LIGHTNINGWARPFUNCTION 0.25*sin(time+i+warpfactor)*(dec/len)
		
		warpadd = LIGHTNINGWARPFUNCTION; //was 0.30*sin(time+warpfactor)
		factor = 1;
		halflen = len/2.0;
		
		thisvec[0]= (thisvec[0]*2 + crandom()*5)/2;
		thisvec[1]= (thisvec[1]*2 + crandom()*5)/2;
		thisvec[2]= (thisvec[2]*2 + crandom()*5)/2;
		
		while (len>dec)
		{	i+=warpsize;
		
			VectorAdd(thisvec, ang_right, angl_coord[0]);
			VectorAdd(lastvec, ang_right, angl_coord[1]);
			VectorSubtract(lastvec, ang_right, angl_coord[2]);
			VectorSubtract(thisvec, ang_right, angl_coord[3]);
			
			qglPushMatrix();
			{
				qglBegin (GL_QUADS);
				{
					qglTexCoord2f (0+warpadd, 1);
					qglVertex3fv  (angl_coord[0]);
					qglTexCoord2f (0+oldwarpadd, 0);
					qglVertex3fv  (angl_coord[1]);
					qglTexCoord2f (1+oldwarpadd, 0); 
					qglVertex3fv  (angl_coord[2]);
					qglTexCoord2f (1+warpadd, 1);
					qglVertex3fv  (angl_coord[3]);
				}
				qglEnd ();
				
			}
			qglPopMatrix ();
			
			tlen = (len<halflen)? fabs(len-halflen): fabs(len-halflen);
			factor = tlen/(size*size);
			
			//if (factor > 1+size/3.0)
			//	factor = 1+size/3.0;
			if (factor > 4)
				factor = 4;
			else if (factor < 1)
				factor = 1;
			
			oldwarpadd = warpadd;
			warpadd = LIGHTNINGWARPFUNCTION; // was 0.30*sin(time+i+warpfactor)
			
			len-=dec;
			VectorCopy(thisvec, lastvec);
			VectorAdd(tempvec, move, tempvec);
			VectorAdd(thisvec, move, thisvec);
			thisvec[0]= ((thisvec[0] + crandom()*size) + tempvec[0]*factor)/(factor+1);
			thisvec[1]= ((thisvec[1] + crandom()*size) + tempvec[1]*factor)/(factor+1);
			thisvec[2]= ((thisvec[2] + crandom()*size) + tempvec[2]*factor)/(factor+1);
		}
		
		i+=warpsize;
		
		// one more time
		if (len>0)
		{
			VectorAdd(p->origin, ang_right, angl_coord[0]);
			VectorAdd(lastvec, ang_right, angl_coord[1]);
			VectorSubtract(lastvec, ang_right, angl_coord[2]);
			VectorSubtract(p->origin, ang_right, angl_coord[3]);
			
			qglPushMatrix();
			{
				qglBegin (GL_QUADS);
				{
					qglTexCoord2f (0+warpadd, 1);
					qglVertex3fv  (angl_coord[0]);
					qglTexCoord2f (0+oldwarpadd, 0);
					qglVertex3fv  (angl_coord[1]);
					qglTexCoord2f (1+oldwarpadd, 0); 
					qglVertex3fv  (angl_coord[2]);
					qglTexCoord2f (1+warpadd, 1);
					qglVertex3fv  (angl_coord[3]);
				}
				qglEnd ();
				
			}
			qglPopMatrix ();
		}
	}
	else if (p->flags&PART_DIRECTION) //streched out in direction...beams etc...
	{
		//float len, dot;
		vec3_t angl_coord[4];
		vec3_t ang_up, ang_right, vdelta;
		
		VectorAdd(p->angle, p->origin, vdelta); 
		setBeamAngles(vdelta, p->origin, ang_up, ang_right);
				
		VectorScale (ang_right, 0.75f, ang_right);
		VectorScale (ang_up, 0.75f * VectorLength(p->angle), ang_up);
		
		VectorAdd      (ang_up, ang_right, angl_coord[0]);
		VectorSubtract (ang_right, ang_up, angl_coord[1]);
		VectorNegate   (angl_coord[0], angl_coord[2]);
		VectorNegate   (angl_coord[1], angl_coord[3]);
		
		qglPushMatrix();
		{
			qglTranslatef( p->origin[0], p->origin[1], p->origin[2] );
			qglScalef(size, size, size );
			
			qglBegin (GL_QUADS);
			{
				qglTexCoord2f (0, 1);
				qglVertex3fv  (angl_coord[0]);
				qglTexCoord2f (0, 0);
				qglVertex3fv  (angl_coord[1]);
				qglTexCoord2f (1, 0); 
				qglVertex3fv  (angl_coord[2]);
				qglTexCoord2f (1, 1);
				qglVertex3fv  (angl_coord[3]);
			}
			qglEnd ();
		}
		qglPopMatrix ();
	}
	else if (p->flags&PART_ANGLED) //facing direction...
	{
		vec3_t angl_coord[4];
		vec3_t ang_up, ang_right, ang_forward;
		
		AngleVectors(p->angle, ang_forward, ang_right, ang_up); 
		
		VectorScale (ang_right, 0.75f, ang_right);
		VectorScale (ang_up, 0.75f, ang_up);
		
		VectorAdd      (ang_up, ang_right, angl_coord[0]);
		VectorSubtract (ang_right, ang_up, angl_coord[1]);
		VectorNegate   (angl_coord[0], angl_coord[2]);
		VectorNegate   (angl_coord[1], angl_coord[3]);
		
		qglPushMatrix();
		{
			qglTranslatef( p->origin[0], p->origin[1], p->origin[2] );
			qglScalef(size, size, size );
			
			qglBegin (GL_QUADS);
			{
				qglTexCoord2f (0, 1);
				qglVertex3fv  (angl_coord[0]);
				qglTexCoord2f (0, 0);
				qglVertex3fv  (angl_coord[1]);
				qglTexCoord2f (1, 0); 
				qglVertex3fv  (angl_coord[2]);
				qglTexCoord2f (1, 1);
				qglVertex3fv  (angl_coord[3]);
			}
			qglEnd ();
		}
		qglPopMatrix ();
	}
	else //normal sprites
	{
		qglPushMatrix();
		{
			qglTranslatef( p->origin[0], p->origin[1], p->origin[2] );
			qglScalef(size, size, size );
			
			qglBegin (GL_QUADS);
			{
				if (p->angle[2]) //if we have roll, we do calcs
				{
					vec3_t angl_coord[4];
					vec3_t ang_up, ang_right, ang_forward;
					
					VectorSubtract(p->origin, r_newrefdef.vieworg, angl_coord[0]);
					
					vectoanglerolled(angl_coord[0], p->angle[2], angl_coord[1]);
					AngleVectors(angl_coord[1], ang_forward, ang_right, ang_up); 
					
					VectorScale (ang_forward, 0.75f, ang_forward);
					VectorScale (ang_right, 0.75f, ang_right);
					VectorScale (ang_up, 0.75f, ang_up);
					
					VectorAdd	   (ang_up, ang_right, angl_coord[0]);
					VectorSubtract (ang_right, ang_up, angl_coord[1]);
					VectorNegate   (angl_coord[0], angl_coord[2]);
					VectorNegate   (angl_coord[1], angl_coord[3]);
					
					qglTexCoord2f (0, 1);
					qglVertex3fv  (angl_coord[0]);
					qglTexCoord2f (0, 0);
					qglVertex3fv  (angl_coord[1]);
					qglTexCoord2f (1, 0); 
					qglVertex3fv  (angl_coord[2]);
					qglTexCoord2f (1, 1);
					qglVertex3fv  (angl_coord[3]);
				}
				else
				{
					qglTexCoord2f (0, 1);
					qglVertex3fv  (coord[0]);
					qglTexCoord2f (0, 0);
					qglVertex3fv  (coord[1]);
					qglTexCoord2f (1, 0); 
					qglVertex3fv  (coord[2]);
					qglTexCoord2f (1, 1);
					qglVertex3fv  (coord[3]);
				}
			}
			qglEnd ();
		}
		qglPopMatrix ();
	}

	if (p->flags&PART_OVERBRIGHT)
		SetParticleOverbright(false);

	if (p->flags&PART_DEPTHHACK)
		qglDepthRange (gldepthmin, gldepthmax);
}


/*
===============
R_DrawAllParticles
===============
*/
void R_DrawAllParticles (void)
{
	int		i;
	particle_t	*p;
	qboolean fog_on = false;

	vec3_t		up		= {vup[0]    * 0.75f, vup[1]    * 0.75f, vup[2]    * 0.75f};
	vec3_t		right	= {vright[0] * 0.75f, vright[1] * 0.75f, vright[2] * 0.75f};

	VectorAdd      (up, right, particle_coord[0]);
	VectorSubtract (right, up, particle_coord[1]);
	VectorNegate   (particle_coord[0], particle_coord[2]);
	VectorNegate   (particle_coord[1], particle_coord[3]);

	// no fog on particles
	if (qglIsEnabled(GL_FOG)) // check if fog is enabled
	{
		fog_on = true;
		qglDisable(GL_FOG); // if so, disable it
	}

	qglEnable( GL_TEXTURE_2D );
	GL_TexEnv( GL_MODULATE );
	qglDepthMask   (false);
	//qglBlendFunc   (GL_SRC_ALPHA, GL_ONE);
	qglEnable( GL_BLEND );
	GL_ShadeModel (GL_SMOOTH);
	ParticleOverbright = false;

	for ( i=0; i < r_newrefdef.num_particles; i++)
	{
		if (gl_transrendersort->value && !(r_newrefdef.rdflags & RDF_NOWORLDMODEL))
		{
			p = theoldparts[i].p;
			if ( gl_particledistance->value > 0 && theoldparts[i].len>(100.0*gl_particledistance->value))
				continue;
		}
		else
			p=&r_newrefdef.particles[i];
		renderParticle(p);
	}
	qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GL_TexEnv( GL_MODULATE );
	qglDepthMask (true);
	qglDisable ( GL_BLEND );
	qglColor4f   (1,1,1,1);

	// re-enable fog if it was on
	if (fog_on)
		qglEnable(GL_FOG);
}


#ifdef BINARY_PART_SORT
/*
===============
R_DrawParticles
===============
*/
void R_DrawParticles (sortedelement_t *list)
{
	qboolean fog_on = false;

	vec3_t		up		= {vup[0]    * 0.75f, vup[1]    * 0.75f, vup[2]    * 0.75f};
	vec3_t		right	= {vright[0] * 0.75f, vright[1] * 0.75f, vright[2] * 0.75f};

	VectorAdd      (up, right, particle_coord[0]);
	VectorSubtract (right, up, particle_coord[1]);
	VectorNegate   (particle_coord[0], particle_coord[2]);
	VectorNegate   (particle_coord[1], particle_coord[3]);

	// no fog on particles
	if (qglIsEnabled(GL_FOG)) // check if fog is enabled
	{
		fog_on = true;
		qglDisable(GL_FOG); // if so, disable it
	}

	qglEnable( GL_TEXTURE_2D );
	GL_TexEnv( GL_MODULATE );
	qglDepthMask   (false);
	//qglBlendFunc   (GL_SRC_ALPHA, GL_ONE);
	qglEnable( GL_BLEND );
	GL_ShadeModel (GL_SMOOTH);

	RenderParticleTree(list);

	qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GL_TexEnv( GL_MODULATE );
	qglDepthMask (true);
	qglDisable ( GL_BLEND );
	qglColor4f   (1,1,1,1);

	// re-enable fog if it was on
	if (fog_on)
		qglEnable(GL_FOG);
}
#endif // BINARY_PART_SORT


/*
===================================================

DECAL RENDERING

===================================================
*/

#ifdef DECALS
/*
===============
renderDecal
===============
*/
vec3_t ParticleVec[4];
vec3_t shadelight;

void renderDecal (particle_t *p)
{
	float size, alpha;
	vec3_t angl_coord[4];
	vec3_t ang_up, ang_right, ang_forward, color;

	size = (p->size>0.1) ? p->size : 0.1;
	alpha = p->alpha;

	GL_BlendFunction (p->blendfunc_src, p->blendfunc_dst);

	GL_Bind(texParticle(p->image));

	if (p->flags & PART_SHADED)
	{
		getParticleLight (p, p->origin, gl_particle_lighting->value, shadelight);
		VectorSet(color, shadelight[0]/255.0, shadelight[1]/255.0, shadelight[2]/255.0);
	}
	else
	{
		VectorSet(shadelight, p->red, p->green, p->blue);
		VectorSet(color, p->red/255.0, p->green/255.0, p->blue/255.0);
	}

	if (p->flags & PART_ALPHACOLOR)
		qglColor4f(color[0]*alpha,color[1]*alpha,color[2]*alpha, alpha);
	else
		qglColor4f(color[0],color[1],color[2], alpha);

	if (!p->decal)
	{
		AngleVectors(p->angle, ang_forward, ang_right, ang_up); 

		VectorScale (ang_right, 0.75f, ang_right);
		VectorScale (ang_up, 0.75f, ang_up);

		VectorAdd      (ang_up, ang_right, angl_coord[0]);
		VectorSubtract (ang_right, ang_up, angl_coord[1]);
		VectorNegate   (angl_coord[0], angl_coord[2]);
		VectorNegate   (angl_coord[1], angl_coord[3]);
		
		VectorMA(p->origin, size, angl_coord[0], ParticleVec[0]);
		VectorMA(p->origin, size, angl_coord[1], ParticleVec[1]);
		VectorMA(p->origin, size, angl_coord[2], ParticleVec[2]);
		VectorMA(p->origin, size, angl_coord[3], ParticleVec[3]);
	}

	qglPushMatrix();
	if (p->decal)
	{
		qglEnable(GL_POLYGON_OFFSET_FILL); 
		qglPolygonOffset(-2, -1); 
		
		qglBegin (GL_TRIANGLE_FAN);
		{
			int i;
			for (i = 0; i < p->decal->numpolys; i++)
			{
				qglTexCoord2f (p->decal->coords[i][0], p->decal->coords[i][1]);
				qglVertex3fv  (p->decal->polys[i]);
			}
		}
		qglEnd ();
		
		qglDisable(GL_POLYGON_OFFSET_FILL);
	}
	else
	{
		qglBegin (GL_QUADS);
		{
			qglTexCoord2f (0, 1);
			qglVertex3fv  (ParticleVec[0]);
			qglTexCoord2f (0, 0);
			qglVertex3fv  (ParticleVec[1]);
			qglTexCoord2f (1, 0); 
			qglVertex3fv  (ParticleVec[2]);
			qglTexCoord2f (1, 1);
			qglVertex3fv  (ParticleVec[3]);
		}
		qglEnd ();
	}
	qglPopMatrix ();
}


/*
===============
R_DrawAllDecals
===============
*/
vec3_t particle_coord[4];

void R_DrawAllDecals (void)
{	
	vec3_t	up		= {vup[0]    * 0.75f, vup[1]    * 0.75f, vup[2]    * 0.75f};
	vec3_t	right	= {vright[0] * 0.75f, vright[1] * 0.75f, vright[2] * 0.75f};
	int		i;
	particle_t *d;

	VectorAdd      (up, right, particle_coord[0]);
	VectorSubtract (right, up, particle_coord[1]);
	VectorNegate   (particle_coord[0], particle_coord[2]);
	VectorNegate   (particle_coord[1], particle_coord[3]);
	
	qglEnable (GL_TEXTURE_2D);
	GL_TexEnv( GL_MODULATE );
	qglDepthMask   (false);
	GLSTATE_ENABLE_BLEND
	GL_ShadeModel (GL_SMOOTH);	
	GLSTATE_DISABLE_ALPHATEST

	for ( i=0; i < r_newrefdef.num_decals; i++)
	{
		d = &r_newrefdef.decals[i];
		if (d->decal) // skip if not going to be visible
			if ((d->decal->node == NULL) || (d->decal->node->visframe != r_visframecount))
				continue;
		renderDecal(d);
	}

	qglDepthRange (gldepthmin, gldepthmax);
	GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	GL_TexEnv( GL_MODULATE );
	qglDepthMask (true);
	GLSTATE_DISABLE_BLEND
	qglColor4f   (1,1,1,1);
}
#endif
