//rendercar.cpp
//code to render a car

#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <math.h>
#include "carphysics.h"
#include "vectors.h"
#include "models.h"
#include "entities.h"
#include "fileio.h"
#include "textures.h"
#include "renderframe.h"
#include "screen.h"
#include "collision.h"
#include "environment.h"
#include "text.h"
#include "config.h"
#include "gameinitexit.h"
#include "roads.h"
#include "rendercar.h"
#include "gameframe.h"
#include "config.h"
#include "transparency.h"
#include "stencil.h"
#include "random.h"

#define sign(x) ((x)<0?-1:1)


#define kCarMinShadow 3

void RenderGauge(tGauge *gauge,float value,float xPos,float yPos,float size,float opacity,float redline)
{
	gTexturesQualityModifier=-255;
	glPushMatrix();

	glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_TEXTURE_BIT+GL_CURRENT_BIT);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);									
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);
	glColor4f(gEnvironment->instrumentColor.x,gEnvironment->instrumentColor.y,gEnvironment->instrumentColor.z,opacity*(1-gConfig->hudTransparency));

	glTranslatef(xPos,yPos,0);
	glScalef(size*0.5,size*0.5,0);

	if(redline)
	{
		float redlineAngle=(gauge->gaugeZero-2*gauge->gaugeZero*(redline/gauge->gaugeMax)+90)*kDegreeRadians;
		
		float t=tanf(-redlineAngle);
		
		TexturesSelectTex(FileGetReference("tachoredline.tif"));
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
		
		glBegin(GL_TRIANGLES);								
		glTexCoord2d(0.5,0.5); glVertex2f(0,0);		
		glTexCoord2d(1,1); glVertex2f(1,-1);		
		glTexCoord2d(1,0.5-0.5*t); glVertex2f(1,t);		
		glEnd();
	}
	
	TexturesSelectTex(gauge->gaugeTexture);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

	glBegin(GL_TRIANGLE_STRIP);								
	glTexCoord2d(1,1); glVertex2f(1,-1);		
	glTexCoord2d(1,0); glVertex2f(1,1); 	
	glTexCoord2d(0,1); glVertex2f(-1,-1); 	
	glTexCoord2d(0,0); glVertex2f(-1,1); 	
	glEnd();
	
	float tachoAngle=(gauge->gaugeZero-2*gauge->gaugeZero*(value/gauge->gaugeMax))*kDegreeRadians;
	float width=gauge->pointerWidth;
	float c=cos(tachoAngle);
	float s=sin(tachoAngle);
	
	TexturesSelectTex(gauge->pointerTexture);

	glBegin(GL_TRIANGLE_STRIP);								
	glTexCoord2d(1,1); glVertex2f(-s+c*width,-c-s*width);		
	glTexCoord2d(1,0); glVertex2f(s+c*width,c-s*width); 	
	glTexCoord2d(0,1); glVertex2f(-s-c*width,-c+s*width); 	
	glTexCoord2d(0,0); glVertex2f(s-c*width,c+s*width); 	
	glEnd();
	
	glPopAttrib();
	glPopMatrix();
	gTexturesQualityModifier=0;
}

#define kPanelSize 0.32
#define kPanelOffsetX -0.02
#define kPanelOffsetY 0.08
//Render the car status panels (tacho,speedo) on screen
void CarRenderPanels(tGameEntity *carEntity,float opacity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);
	tGaugesList *gauges=(tGaugesList*)FileGetParsedDataPtr(FileGetReference(kGaugesFileName),kParserTypeGaugesListDesc,sizeof(tGaugesList));
	float width,c,s;
	
//Tacho
	int tachoGauge=-1;
	float gaugeFit=INFINITY;
	for(int i=0;i<gauges->numGauges;i++)
		if(gauges->gauges[i].type==kGaugeTypeTacho)
		{
			int fit=gauges->gauges[i].gaugeMax-car->maxRPM;
			if(fit>=0&&fit<gaugeFit)
			{
				gaugeFit=fit;
				tachoGauge=i;
			}
		}	
	if(tachoGauge==-1)
		for(int i=0;i<gauges->numGauges;i++)
			if(gauges->gauges[i].type==kGaugeTypeTacho)
			{
				int fit=fabs(gauges->gauges[i].gaugeMax-car->maxRPM);
				if(fit<gaugeFit)
				{
					gaugeFit=fit;
					tachoGauge=i;
				}
			}	
	if(tachoGauge!=-1)
		RenderGauge(gauges->gauges+tachoGauge,phys->rpm,(0.5-kPanelOffsetX-0.5*kPanelSize),-(0.5-kPanelOffsetY-0.5*kPanelSize),kPanelSize,opacity,car->maxRPM*0.9);
			
//Speedo
	int speedoGauge=-1;
	float maxV=car->maxRPM/(car->gearRatios[car->numGears-1]*car->finalDriveRatio)*car->wheels[0].radius*2*PI/60*0.9;
	gaugeFit=INFINITY;
	for(int i=0;i<gauges->numGauges;i++)
		if(gauges->gauges[i].type==(gConfig->metricUnits?kGaugeTypeSpeedoMetric:kGaugeTypeSpeedo))
		{
			int fit=gauges->gauges[i].gaugeMax-maxV;
			if(fit>=0&&fit<gaugeFit)
			{
				gaugeFit=fit;
				speedoGauge=i;
			}
		}
	if(speedoGauge==-1)
		for(int i=0;i<gauges->numGauges;i++)
			if(gauges->gauges[i].type==(gConfig->metricUnits?kGaugeTypeSpeedoMetric:kGaugeTypeSpeedo))
				speedoGauge=i;
	if(speedoGauge!=-1)	
		RenderGauge(gauges->gauges+speedoGauge,~carEntity->velo,-(0.5-kPanelOffsetX-0.5*kPanelSize),-(0.5-kPanelOffsetY-0.5*kPanelSize),kPanelSize,opacity,0);
			

//Selected Gear
	if(phys->gear==-1)
		TextPrintfToBufferFormatedColored(Vector(0.8,-0.9),0.07,kTextAlignLeft,1,1,1,opacity,"R");
	else if(phys->gear==0)
		TextPrintfToBufferFormatedColored(Vector(0.8,-0.9),0.07,kTextAlignLeft,1,1,1,opacity,"N");
/*	else if(gGameInfo->playerAutomatic)
		TextPrintfToBufferFormated(Vector(0.8,-0.9),0.07,kTextAlignLeft,"D");*/
	else
		TextPrintfToBufferFormatedColored(Vector(0.8,-0.9),0.07,kTextAlignLeft,1,1,1,opacity,"%d",phys->gear);
	
	#ifdef __POLYCOUNT
	gPolyCount+=8;
	#endif

}

#define kShiftAnimationTime 0.3
#define kArmNumEdges 6
#define kArmRadius 0.04

void RenderArm(tVector3 point1,tVector3 point2,tVector3 point3)
{
	tVector3 dir12=!(point2-point1);
	tVector3 dir13=!(point3-point1);
	tVector3 dir23=!(point3-point2);
	tVector3 dir1=!(dir12%Vector(0,1,0));
	tVector3 dir2=!(dir13%Vector(0,1,0));
	tVector3 dir3=!(dir23%Vector(0,1,0));
	tMatrix3 mat1;
	tMatrix3 mat2;
	tMatrix3 mat3;
	RotationVectorToMatrix(-dir12*((2*PI)/(kArmNumEdges)),mat1);
	RotationVectorToMatrix(-dir13*((2*PI)/(kArmNumEdges)),mat2);
	RotationVectorToMatrix(-dir23*((2*PI)/(kArmNumEdges)),mat3);
	for(int i=0;i<kArmNumEdges;i++)
	{
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3fv(&(point1).x);
		glNormal3fv(&dir1.x);
		glVertex3fv(&(point1+dir1*kArmRadius).x);
		dir1=dir1*mat1;
		glNormal3fv(&dir1.x);
		glVertex3fv(&(point1+dir1*kArmRadius).x);
		glNormal3fv(&dir2.x);
		glVertex3fv(&(point2+dir2*kArmRadius).x);
		dir2=dir2*mat2;
		glNormal3fv(&dir2.x);
		glVertex3fv(&(point2+dir2*kArmRadius).x);
		glNormal3fv(&dir3.x);
		glVertex3fv(&(point3+dir3*kArmRadius).x);
		dir3=dir3*mat3;
		glNormal3fv(&dir3.x);
		glVertex3fv(&(point3+dir3*kArmRadius).x);
		glVertex3fv(&(point3).x);
		glEnd();
	}
}

void CarRenderDriver(tGameEntity *carEntity)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);

	if(car->noDriverModel!=1)
	{
		tVector3 innerShoulder=Vector(-0.13*sign(car->driverPos.x),0.365,-0.047)+car->driverPos;
		tVector3 outerShoulder=Vector(0.13*sign(car->driverPos.x),0.365,-0.047)+car->driverPos;
		tMatrix3 steeringDir;
		MatrixIdentity(steeringDir);
		MatrixRotZ(steeringDir,-car->steeringWheelTurns*PI*phys->steering);
		MatrixRotX(steeringDir,car->steeringWheelAngle*(2*PI/360.0));

		//hands on steering wheel
		tVector3 innerHand=Vector(-car->steeringWheelRadius*sign(car->driverPos.x),0,0)*steeringDir+car->steeringWheelPos;
		tVector3 outerHand=Vector(car->steeringWheelRadius*sign(car->driverPos.x),0,0)*steeringDir+car->steeringWheelPos;
				
		//hand on handbrake
		if(phys->handbrake)
			innerHand=Vector(car->driverPos.x-sign(car->driverPos.x)*0.47
								,car->driverPos.y-0.3+0.3*phys->handbrake
								,car->driverPos.z+0.3);
		
		//hand on shifter
		float shiftDelay=gFrameCount*kFrameTime-phys->lastGearSwitch;
		if(shiftDelay<kShiftAnimationTime)
			innerHand=Vector(car->driverPos.x-sign(car->driverPos.x)*0.47
								,car->driverPos.y+0.13
								,car->driverPos.z+0.6-0.3*(shiftDelay/kShiftAnimationTime));
		
		float maxArmLength=~(car->steeringWheelPos-outerShoulder);
		tVector3 innerArm=innerHand-innerShoulder;
		tVector3 outerArm=outerHand-outerShoulder;
		float innerArmLength=~innerArm;
		float outerArmLength=~outerArm;
		float innerArmBend=innerArmLength>=maxArmLength?0:sqrt(sqr(maxArmLength)-sqr(innerArmLength))*0.5;
		float outerArmBend=outerArmLength>=maxArmLength?0:sqrt(sqr(maxArmLength)-sqr(outerArmLength))*0.5;
		tVector3 innerElbow=innerShoulder+innerArm*0.5+((innerHand.z>innerShoulder.z)?1:-1)*!((innerArm)%Vector(0,1,0))*innerArmBend;
		tVector3 outerElbow=outerShoulder+outerArm*0.5-((outerHand.z>outerShoulder.z)?1:-1)*!((outerArm)%Vector(0,1,0))*outerArmBend;

		glPushAttrib(GL_LIGHTING_BIT+GL_CURRENT_BIT+GL_TEXTURE_BIT);	
		TexturesSelectTextureUnit(2);
		glDisable(GL_TEXTURE_2D);
		TexturesSelectTextureUnit(1);
		glDisable(GL_TEXTURE_2D);
		TexturesSelectTextureUnit(0);
		glDisable(GL_TEXTURE_2D);
		GLfloat col[4]={0.1,0.1,0.1,1};
		glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,col);
		RenderArm(innerShoulder,innerElbow,innerHand);
		RenderArm(outerShoulder,outerElbow,outerHand);
		glPopAttrib();
	}
	
	if(!car->noDriverModel){
		tVector3 driverPos=car->driverPos*carEntity->dir+carEntity->pos;	
		SetupTranslation(driverPos,carEntity->dir);
		DrawModel(FileGetReference("driver.mdl"),true,0);
	}
}



void CarRenderEntityGhost(tGameEntity *carEntity)
{
	gGhostShine=true;
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);
	
	//draw the car model
	if(gConfig->interiorDisplay||gCameraMode==kCameraCockpitCarHidden)
		DrawModel(car->model,kTransparencyGhost,phys->color);
	else
		DrawModel(car->model,kTransparencyOff,phys->color);
	
	//draw add-ons.
	for(int i=0;i<car->numAddOns;i++)
		//is this add-on installed?
		if(((phys->addOns|car->initialAddOns)>>i)&1)
			//do we need to draw a model?
			if(car->addOns[i].hasGraphic)
				if(gConfig->interiorDisplay>0||gCameraMode==kCameraCockpitCarHidden)
					DrawModel(car->addOns[i].model,kTransparencyGhost,phys->color);
				else
					DrawModel(car->addOns[i].model,kTransparencyOff,phys->color);
				
	//draw the car's wheels
	for(int i=0;i<car->numWheels;i++){
		tMatrix3 wheelDir,brakeDir;
		MatrixIdentity(wheelDir);
		
		//correctly rotate wheel
		MatrixScale(wheelDir,car->wheels[i].width/1,car->wheels[i].radius/1,car->wheels[i].radius/1);
		MatrixCopy(wheelDir,brakeDir);
		MatrixRotX(wheelDir,-sign(car->wheels[i].pos.x)*phys->wheels[i].rotation);
		MatrixRotY(wheelDir,(car->wheels[i].pos.x<0?0:PI)+phys->wheels[i].angle);
		MatrixRotZ(wheelDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt);
		MatrixMult(wheelDir,carEntity->dir,wheelDir);
		
		tVector3 wheelPos=car->wheels[i].pos;
		wheelPos.y-=phys->wheels[i].suspension;
		wheelPos=wheelPos*carEntity->dir+carEntity->pos;
		SetupTranslation(wheelPos,wheelDir);
		
		gGlowFactor=phys->wheels[i].glow;
		gBlurMapFactor=fabs(phys->wheels[i].angularVelo/(2*PI*12));
		if(gBlurMapFactor>1)gBlurMapFactor=1;

		if(gConfig->interiorDisplay>0||gCameraMode==kCameraCockpitCarHidden)
			DrawModel(car->wheels[i].model,kTransparencyGhost,car->wheels[i].texture);
		else
			DrawModel(car->wheels[i].model,kTransparencyOff,car->wheels[i].texture);
	}
	gGhostShine=false;
}

void DrawLicensePlate(char *text)
{
	GLfloat specularReflectance[4]={1,1,1,1};
	GLfloat ambientReflectance[4]={0.75,0.75,0.75,1};
	GLfloat diffuseReflectance[4]={0.8,0.8,0.8,1};
	glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuseReflectance);
	glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambientReflectance);
	glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specularReflectance);
	glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,30);

	glNormal3f(0,0,1);
	TexturesSelectTex(FileGetReference("license.pct"));
	glBegin(GL_TRIANGLE_STRIP);								
	glTexCoord2d(0,0); glVertex3f(-0.3,-0.18,-0.02); 	
	glTexCoord2d(1,0); glVertex3f(0.3,-0.18,-0.02); 	
	glTexCoord2d(0,1); glVertex3f(-0.3,0.0,-0.02); 	
	glTexCoord2d(1,1); glVertex3f(0.3,0.0,-0.02);		
	glEnd();
	
	glDepthMask(0);
	TextDrawSimple(text,0.56,0.18,kTextAlignMiddle);
	glDepthMask(1);
}

//render a car.
void CarRenderEntity(tGameEntity *carEntity,int allowTransparency,int showDriver)
{
	gTexturesQualityModifier=-2;
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);

	if(car->numColors>phys->color)
		car->colorLoaded[phys->color]=true;
	
	int drawDirt=gEnvironment->dirtEnable;
	if(gMapInfo)
		if(gMapInfo->dirtEnable)
			drawDirt=true;
	if(drawDirt)
		if(TexturesSelectTextureUnit(2))
		{
			glEnable(GL_TEXTURE_2D);
			glEnable(GL_TEXTURE_GEN_S);
			glEnable(GL_TEXTURE_GEN_T);
			TexturesSelectTex(gMapInfo->dirtEnable?gMapInfo->dirtMap:gEnvironment->dirtMap);
			glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
			glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
			float extends=ModelGetMaxExtends(car->model);
			GLfloat plane1[4]={phys->dirtStretch[0]/extends,0,phys->dirtStretch[1]/extends,0};
			GLfloat plane2[4]={0.5*phys->dirtStretch[2]/extends,0.5*phys->dirtStretch[3]/extends,phys->dirtStretch[4]/extends,0};
			glTexGenfv(GL_S,GL_OBJECT_PLANE,plane1);
			glTexGenfv(GL_T,GL_OBJECT_PLANE,plane2);

			GLfloat color[4]={1,1,1,phys->dirt*0.01*(gMapInfo->dirtEnable?gMapInfo->dirtIntensity:gEnvironment->dirtIntensity)};
			glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_ARB);
			glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB_ARB,GL_INTERPOLATE_ARB);	
			glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB_ARB,GL_CONSTANT_ARB);	
			glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB_ARB,GL_SRC_ALPHA);	
			glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,color);	
				
			TexturesSelectTextureUnit(0);
		}
	
	gTunnelFactor=phys->echo;
	//draw the car model
	DrawModel(car->model,allowTransparency,phys->color);
	

	if(drawDirt)
		if(TexturesSelectTextureUnit(2)){
			glDisable(GL_TEXTURE_2D);
			TexturesSelectTextureUnit(0);
		}
	
	//draw add-ons.
	for(int i=0;i<car->numAddOns;i++)
		//is this add-on installed?
		if(((phys->addOns|car->initialAddOns)>>i)&1)
			//do we need to draw a model?
			if(car->addOns[i].hasGraphic)
				DrawModel(car->addOns[i].model,allowTransparency,phys->color);
				
	//if we have transparency, we need to draw the interior
	if(allowTransparency)
	{
		//draw interior model
		DrawModel(car->interiorModel,true,phys->color);

		//if we aren't "inside" the driver, render him.
		if(showDriver)
			CarRenderDriver(carEntity);
		
		//and the steering wheel
		
		if(car->steeringWheelRadius)
		{
			tMatrix3 steeringDir;
			tVector3 steeringWheelPos=car->steeringWheelPos*carEntity->dir+carEntity->pos;	
			MatrixIdentity(steeringDir);
			MatrixRotZ(steeringDir,-car->steeringWheelTurns*PI*phys->steering);
			MatrixRotX(steeringDir,car->steeringWheelAngle*(2*PI/360.0));
			MatrixMult(steeringDir,carEntity->dir,steeringDir);
			SetupTranslation(steeringWheelPos,steeringDir);

			if(tTransparentPoly *p=GetNextTransparentPoly(true)){
				p->v[0].texel=Vector(0,1);
				p->v[1].texel=Vector(0,0);
				p->v[2].texel=Vector(1,1);
				p->v[0].normal=Vector(0,0,1)*gTransformDir;
				p->v[1].normal=Vector(0,0,1)*gTransformDir;
				p->v[2].normal=Vector(0,0,1)*gTransformDir;
				p->v[0].vertex=Vector(-car->steeringWheelRadius,-car->steeringWheelRadius,0)*gTransformDir+gTransformPos;
				p->v[1].vertex=Vector(-car->steeringWheelRadius,car->steeringWheelRadius,0)*gTransformDir+gTransformPos;
				p->v[2].vertex=Vector(car->steeringWheelRadius,-car->steeringWheelRadius,0)*gTransformDir+gTransformPos;
				p->mat->flags=kMaterialBothSides+kMaterialUseAlphaChannel+kMaterialDisableWrapS;
				p->mat->shininess=0;
				p->mat->specular=Vector(0,0,0);
				p->mat->ambient=Vector(0.6,0.6,0.6);
				p->mat->diffuse=Vector(0.4,0.4,0.4);
				p->mat->texRef=car->steeringWheelTexture;
				if(tTransparentPoly *p2=GetNextTransparentPoly(false)){
					p2->v[0].texel=Vector(0,0);
					p2->v[1].texel=Vector(1,1);
					p2->v[2].texel=Vector(1,0);
					p2->v[0].normal=Vector(0,0,1)*gTransformDir;
					p2->v[1].normal=Vector(0,0,1)*gTransformDir;
					p2->v[2].normal=Vector(0,0,1)*gTransformDir;
					p2->v[0].vertex=Vector(-car->steeringWheelRadius,car->steeringWheelRadius,0)*gTransformDir+gTransformPos;
					p2->v[1].vertex=Vector(car->steeringWheelRadius,-car->steeringWheelRadius,0)*gTransformDir+gTransformPos;
					p2->v[2].vertex=Vector(car->steeringWheelRadius,car->steeringWheelRadius,0)*gTransformDir+gTransformPos;
					p2->mat=p->mat;
				}
			}
		}
	}
	
	//draw License plates
	if(phys->plateName)
	{
		TextLoadFont(FileGetReference("license.font"));
		if(!VectorZero(car->frontLicensePlatePos))
		{
			tVector3 licensePlatePos=car->frontLicensePlatePos*carEntity->dir+carEntity->pos;
			SetupTranslation(licensePlatePos,carEntity->dir);
			DrawLicensePlate(phys->plateName);
		}
		if(!VectorZero(car->rearLicensePlatePos))
		{
			tVector3 licensePlatePos=car->rearLicensePlatePos*carEntity->dir+carEntity->pos;
			tMatrix3 licenseDir;
			MatrixIdentity(licenseDir);
			MatrixRotY(licenseDir,PI);
			MatrixMult(licenseDir,carEntity->dir,licenseDir);
			SetupTranslation(licensePlatePos,licenseDir);
			DrawLicensePlate(phys->plateName);
		}
		TextLoadFont(FileGetReference("test.font"));
	}
	
	gTexturesQualityModifier=-4;
	//draw the car's wheels
	for(int i=0;i<car->numWheels;i++){
		tMatrix3 wheelDir,brakeDir;
		MatrixIdentity(wheelDir);
		
		//correctly rotate wheel
		MatrixScale(wheelDir,car->wheels[i].width/1,car->wheels[i].radius/1,car->wheels[i].radius/1);
		MatrixCopy(wheelDir,brakeDir);
		MatrixRotX(wheelDir,-sign(car->wheels[i].pos.x)*phys->wheels[i].rotation);
		MatrixRotY(wheelDir,(car->wheels[i].pos.x<0?0:PI)+phys->wheels[i].angle);
		MatrixRotZ(wheelDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt);
		MatrixMult(wheelDir,carEntity->dir,wheelDir);
		
		tVector3 wheelPos=car->wheels[i].pos;
		wheelPos.y-=phys->wheels[i].suspension;
		wheelPos=wheelPos*carEntity->dir+carEntity->pos;
		SetupTranslation(wheelPos,wheelDir);
		
		gGlowFactor=phys->wheels[i].glow;
		gBlurMapFactor=fabs(phys->wheels[i].angularVelo/(2*PI*12));
		if(gBlurMapFactor>1)gBlurMapFactor=1;

		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		tMatrix4 m;
		MatrixIdentity(m);
		MatrixTranslate(m,0,0,0.25+0.5*(gBlurMapFactor*2<1?gBlurMapFactor*2:1));
		glLoadMatrixf((float*)m);		
		
		DrawModel(car->wheels[i].model,allowTransparency,car->wheels[i].texture);
				
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);

		if(allowTransparency)
		{
			MatrixRotX(brakeDir,(car->wheels[i].pos.x<0?0:PI));
			MatrixRotY(brakeDir,(car->wheels[i].pos.x<0?0:PI)+phys->wheels[i].angle);
			MatrixRotZ(brakeDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt);
			MatrixMult(brakeDir,carEntity->dir,brakeDir);
			SetupTranslation(wheelPos,brakeDir);
			if(car->wheels[i].customBrakeModel>0)
				DrawModel(car->wheels[i].customBrakeModel,allowTransparency,0);
			else
				DrawModel(FileGetReference("brakedischousing.mdl"),allowTransparency,0);
		}
	}
	gTexturesQualityModifier=0;
}

#define kNumWheelSides 12

void CarRenderWheelShadowPass(float width,float radius,float shadowLength,int cw)
{
	tVector3 lightDir=gEnvironment->lightDir;
	if(gMapEnv)
		if(!VectorZero(gMapEnv->lightDir))
			lightDir=gMapEnv->lightDir;
	tMatrix3 tr;
	MatrixTranspose(gTransformDir,tr);
	lightDir=lightDir*tr;
	tVector3 shadowVector=lightDir*shadowLength;
	
	if(lightDir.x!=0)
	{
		tVector3 lightDirFlat=lightDir;
		lightDirFlat.x=0;
		lightDirFlat=!lightDirFlat;
		tVector3 normal=lightDirFlat%Vector(1,0,0);
		
		lightDirFlat=lightDirFlat*radius;
		normal=normal*radius;
		
		float dir=-sign(lightDir.x)*width/2;
		
		glFrontFace(lightDir.x<0?(cw?GL_CW:GL_CCW):(cw?GL_CCW:GL_CW));
	
		glBegin(GL_TRIANGLE_STRIP);
		for(int i=0;i<=kNumWheelSides/2;i++)
		{
			float f=((float)i/kNumWheelSides)*2*PI;
			float s=sin(f);
			float c=cos(f);
			tVector3 v=Vector(dir,c*normal.y+s*lightDirFlat.y,c*normal.z+s*lightDirFlat.z);
			glVertex3fv(&v.x);
			glVertex3fv(&(v-shadowVector).x);			
		}		
		for(int i=kNumWheelSides/2;i<=kNumWheelSides;i++)
		{
			float f=((float)i/kNumWheelSides)*2*PI;
			float s=sin(f);
			float c=cos(f);
			tVector3 v=Vector(-dir,c*normal.y+s*lightDirFlat.y,c*normal.z+s*lightDirFlat.z);
			glVertex3fv(&v.x);
			glVertex3fv(&(v-shadowVector).x);
		}	
		tVector3 v=Vector(dir,normal.y,normal.z);
		glVertex3fv(&v.x);
		glVertex3fv(&(v-shadowVector).x);			
		glEnd();
	}	
	
	#ifdef __POLYCOUNT
	gPolyCount+=kNumWheelSides*2+4;
	#endif
}

void CarRenderWheelShadow(float width,float radius,float shadowLength)
{	
	//disable actual drawing
	glDisable(GL_LIGHTING);
	glDisable(GL_FOG);
	glDepthMask(GL_FALSE);
	glColorMask(0, 0, 0, 0);
	
	//enable stencil buffer drawing
	glEnable(GL_STENCIL_TEST);
	glStencilFunc(GL_ALWAYS, 0, 0xffffffff);

	//zPass Rendering

	glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
	CarRenderWheelShadowPass(width,radius,shadowLength,false);
	
	glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
	CarRenderWheelShadowPass(width,radius,shadowLength,true);
	
	glDepthMask(GL_TRUE);
	glColorMask(1, 1, 1, 1);
}

//draw the car's shadow
void CarRenderEntityShadow(tGameEntity *carEntity,float shadowLength)
{
	tCarPhysics *phys=(tCarPhysics*)carEntity->physics;
	tCarDefinition *car=&(phys->car);
	tFileRef nullModel=FileGetReference("null.mdl");
	if(shadowLength==0)
	{
		shadowLength=GetGroundOffset(carEntity->pos,&carEntity->lastRoadIndex,0,0)+kCarMinShadow;
		shadowLength*=1/(gEnvironment->lightDir*Vector(0,1,0));
	}

	//draw the shadow for the car model
	if(car->shadowModel)
		DrawModelShadow(car->shadowModel,shadowLength);
	else
		DrawModelShadow(car->model,shadowLength);
	
	//draw add-ons shadows.
	for(int i=0;i<car->numAddOns;i++)
		//is this add-on installed?
		if(((phys->addOns|car->initialAddOns)>>i)&1)
			//do we need to draw a model?
			if(car->addOns[i].hasGraphic)
				DrawModelShadow(car->addOns[i].model,shadowLength);

	//draw the shadow for the wheels
	for(int i=0;i<car->numWheels;i++)
		if(car->wheels[i].model!=nullModel&&!car->wheels[i].noShadow)
		{
			tMatrix3 wheelDir;
			MatrixIdentity(wheelDir);
			//MatrixScale(wheelDir,car->wheels[i].widthRatio,1,1);
			MatrixRotX(wheelDir,sign(car->wheels[i].pos.x)*phys->wheels[i].rotation);
			MatrixRotY(wheelDir,PI+phys->wheels[i].angle);
			MatrixRotZ(wheelDir,-sign(car->wheels[i].pos.x)*car->wheels[i].tilt);
			MatrixMult(wheelDir,carEntity->dir,wheelDir);
			tVector3 wheelPos=car->wheels[i].pos;
			wheelPos.y-=phys->wheels[i].suspension;
			wheelPos=wheelPos*carEntity->dir+carEntity->pos;
			SetupTranslation(wheelPos,wheelDir);
			CarRenderWheelShadow(car->wheels[i].width*sqr(gStencilZoom),car->wheels[i].radius*sqr(gStencilZoom),shadowLength);
		}
}

