//renderframe.cpp
//renders one graphics frame

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

#ifdef __POLYCOUNT
int gPolyCount;
#endif

int gCameraMode,gCameraReverse,gClipEnable=true;
tMatrix3 gTransformDir;
tVector3 gTransformPos;

void Beer(tMatrix4 m)
{
	MatrixRotX(m,0.1*sin(gFrameCount*0.01));
	MatrixRotY(m,0.02*sin(gFrameCount*0.1));
	MatrixRotZ(m,0.3*sin(gFrameCount*0.008));
	MatrixScale(m,1+0.1*sin(gFrameCount*0.01),1+0.4*cos(gFrameCount*0.001),1-0.15*sin(gFrameCount*0.02));
	
	tMatrix4 mt;
	MatrixIdentity(mt);
	MatrixRotX(mt,0.01*sin(gFrameCount*0.01));
	MatrixRotY(mt,0.002*sin(gFrameCount*0.1));
	MatrixRotZ(mt,0.03*sin(gFrameCount*0.008));
	glMatrixMode(GL_TEXTURE);
	glLoadMatrixf((float*)mt);	
	glMatrixMode(GL_MODELVIEW);
}

//Set up the Modelview matrix to draw the object at objPos, rotated by objDir
void SetupTranslation(tVector3 objPos,tMatrix3 objDir)
{	
	tMatrix4 m1,m2;
	tMatrix3 m,r,invTransformDir;
	MatrixIdentity(m1);
	MatrixMult(m1,objDir,m2);
	MatrixTranslateVector(m2,objPos-gCameraEntity->pos);
	MatrixTranspose(gCameraEntity->dir,m);
	r[0][0]=-1;r[0][1]=0;r[0][2]=0;
	r[1][0]=0; r[1][1]=1;r[1][2]=0;
	r[2][0]=0; r[2][1]=0;r[2][2]=-1;
	MatrixMult(m,r,m);	
	MatrixMult(m2,m,m1);
#ifndef __TARGET_TOOLAPP
	if(gGameInfo)
		if(gGameInfo->carsOnSpeed)
			Beer(m1);
#endif
	glLoadMatrixf((float*)m1);
	
	MatrixCopy(objDir,gTransformDir);
	gTransformPos=objPos;

	MatrixTranspose(objDir,invTransformDir);
	GLfloat globalLightPosition[4];	
	*(tVector3*)globalLightPosition=gEnvironment->lightDir*invTransformDir;
	if(gMapEnv)
		if(!VectorZero(gMapEnv->lightDir))
			*(tVector3*)globalLightPosition=gMapEnv->lightDir*invTransformDir;

	globalLightPosition[3]=0;
	glLightfv(GL_LIGHT0,GL_POSITION,globalLightPosition);	
	
	if(gViewedEntity)
	{
		float objDist=~(objPos-gViewedEntity->pos);
		if(objDist==0)
			gShineFactor=0;
		else
		{
			gShineFactor=(objPos-gViewedEntity->pos)*(1/objDist)**MatrixGetZVector(gViewedEntity->dir);
		//	gShineFactor=sqr(sqr(sqr(sqr(gShineFactor))))*gShineFactor;
			if(objDist>100)
				gShineFactor-=(objDist-100)*0.005;
			if(gShineFactor<0)gShineFactor=0;
		}
	}
}

//Set up the Modelview matrix to draw objects at world coordinates
void SetupWorldTranslation()
{	
	tMatrix4 m1,m2;
	tMatrix3 m,r;
	MatrixIdentity(m1);
	MatrixTranslateVector(m1,-gCameraEntity->pos);
	MatrixTranspose(gCameraEntity->dir,m);
	r[0][0]=-1;r[0][1]=0;r[0][2]=0;
	r[1][0]=0; r[1][1]=1;r[1][2]=0;
	r[2][0]=0; r[2][1]=0;r[2][2]=-1;
	MatrixMult(m,r,m);	
	MatrixMult(m1,m,m2);
#ifndef __TARGET_TOOLAPP
	if(gGameInfo)
		if(gGameInfo->carsOnSpeed)
			Beer(m2);
#endif
	glLoadMatrixf((float*)m2);
	
	GLfloat globalLightPosition[4];	
	*(tVector3*)globalLightPosition=gEnvironment->lightDir;
	if(gMapEnv)
		if(!VectorZero(gMapEnv->lightDir))
			*(tVector3*)globalLightPosition=gMapEnv->lightDir;
	globalLightPosition[3]=0;
	glLightfv(GL_LIGHT0,GL_POSITION,globalLightPosition);	

	MatrixIdentity(gTransformDir);
	gTransformPos=Vector(0,0,0);
}

//calculates the clipping planes from the camera position and rotation
void SetupClipPlanes(tGameEntity *camera,tVector3 *clipPlanes)
{
	clipPlanes[kClipBasePoint]=Vector(0,0,-kClipSaveDistance);
	clipPlanes[kClipFarPoint]=Vector(0,0,ClipDistance()-kClipSaveDistance);

	clipPlanes[kClipXPlane]=Vector(1,0,0);
	clipPlanes[kClipYPlane]=Vector(0,1,0);
	clipPlanes[kClipRearPlane]=Vector(0,0,1);

	clipPlanes[kClipRightPlane]=Vector(-cos(gFOVX*0.5),0,sin(gFOVX*0.5));
	clipPlanes[kClipLeftPlane]=Vector(-clipPlanes[kClipRightPlane].x,0,clipPlanes[kClipRightPlane].z);

	clipPlanes[kClipTopPlane]=Vector(0,cos(gFOVY*0.5),sin(gFOVY*0.5));
	clipPlanes[kClipBotPlane]=Vector(0,-clipPlanes[kClipTopPlane].y,clipPlanes[kClipTopPlane].z);
	
	for(int i=0;i<kNumClipPlanes;i++)
	{
		clipPlanes[i]=clipPlanes[i]*camera->dir;
	}
	clipPlanes[kClipBasePoint]=clipPlanes[kClipBasePoint]+camera->pos;
}

//tests if the a point is inside the clipping volume given by clipPlanes
int ClipPoint(tVector3 *clipPlanes,tVector3 *point)
{
	if(!gClipEnable)
		return false;

	tVector3 relPoint=*point-clipPlanes[kClipBasePoint];
	int clip=0;
	
	for(int plane=kClipRearPlane;plane<kNumClipPlanes;plane++)
		if(relPoint*clipPlanes[plane]<0)
			clip+=1<<plane;
	
	relPoint=relPoint-clipPlanes[kClipFarPoint];
	
	if(relPoint*clipPlanes[kClipRearPlane]>0)
		clip+=1<<kClipFarPoint;
	
	return clip;
}

//tests if the a point is inside the clipping volume given by clipPlanes,
//or within distance of it (useful as the objects we want to clip are usually bigger
//than a single point in space)
int ClipPointDistanced(tVector3 *clipPlanes,tVector3 *point,float distance)
{
	if(!gClipEnable)
		return false;

	tVector3 relPoint=*point-clipPlanes[kClipBasePoint];
	int clip=0;
	
	for(int plane=kClipRearPlane;plane<kNumClipPlanes;plane++)
		if(relPoint*clipPlanes[plane]<-distance)
			clip+=1<<plane;
	
	relPoint=relPoint-clipPlanes[kClipFarPoint];
	
	if(relPoint*clipPlanes[kClipRearPlane]>distance)
		clip+=1<<kClipFarPoint;
	
	return clip;
}

//swap the position of two entities in the gmae entity list
void SwitchEntities(tGameEntity* obj1,tGameEntity* obj2)
{
	((tGameEntity*)obj1->prev)->next=obj2;
	((tGameEntity*)obj2->next)->prev=obj1;
	obj1->next=obj2->next;
	obj2->prev=obj1->prev;
	obj1->prev=obj2;
	obj2->next=obj1;
}

void SortEntities() 
{
	tGameEntity *entity=(tGameEntity*)gFirstEntity->next;
	while(entity!=gFirstEntity)
	{
		if(entity->renderType==kRenderTypeModel||entity->renderType==kRenderTypeCar||entity->renderType==kRenderTypeGhost)
		{
			tVector3 center=ModelGetCenter(entity->renderData);
			center=center*entity->dir;
			center=center+entity->pos;
			entity->zDist=(center-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir);
		}
		else
			entity->zDist=(entity->pos-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir);		
		entity=(tGameEntity*)entity->next;
	}

	tGameEntity	*start=(tGameEntity*)gFirstEntity->next;
	tGameEntity	*end=gFirstEntity;
	int change;
	do{ //since in most cases there wont be much change a shaking sort is often the fastest
		entity=start;
		change=false;
		while(entity->next!=end)
		{
			tGameEntity *next=(tGameEntity*)entity->next;
			if(next->zDist>entity->zDist)
				{SwitchEntities(entity,next);change=true;}
			else
				entity=next;
		}
		if(change)
		{
			end=(tGameEntity*)end->prev;
			entity=end;
			while(entity->prev!=start)
			{
				tGameEntity *prev=(tGameEntity*)entity->prev;
				if(prev->zDist<entity->zDist)
					SwitchEntities(prev,entity);
				else
					entity=prev;
			}
			start=(tGameEntity*)start->next;
			if(start==end) change=false;
		}
	}while(change);

}

void RenderObjectPass(tVector3 *clipPlanes)
{
	glPushAttrib(GL_LIGHTING_BIT+GL_TEXTURE_BIT+GL_POLYGON_BIT+GL_COLOR_BUFFER_BIT);

	tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->prev;
	while(drawEntity!=gFirstEntity)
	{
		if(drawEntity->renderType==kRenderTypeModel||drawEntity->renderType==kRenderTypeCar||drawEntity->renderType==kRenderTypeGhost)
		{
			float distance=ModelGetCenterMaxExtends(drawEntity->renderData);
			tVector3 center=ModelGetCenter(drawEntity->renderData);
			center=center*drawEntity->dir;
			center=center+drawEntity->pos;
			if(drawEntity->zDist>-distance||!gClipEnable)
				if(!ClipPointDistanced(clipPlanes,&center,distance))
				{
					SetupTranslation(drawEntity->pos,drawEntity->dir);
					switch(drawEntity->renderType)
					{
						case kRenderTypeModel:
							DrawModel(drawEntity->renderData,true,*((int*)drawEntity->physics));
							break;
						case kRenderTypeCar:
							{
								if(gCameraMode==kCameraCockpitCarHidden&&drawEntity==gCarEntities[gReplayViewedEntityID]&&sqr(gCameraEntity->pos-drawEntity->pos)<sqr(1.5))
									break;
								int drawInterior=drawEntity==gViewedEntity;
								float clipInteriorDist=gConfig->gfxDynamics*200.0f;
								if(drawEntity->zDist<clipInteriorDist)
									drawInterior=true;
								if(!gConfig->interiorDisplay)
									drawInterior=false;
								CarRenderEntity(drawEntity,drawInterior,drawEntity->controlType!=kControlTypeNone&&!(drawEntity==gViewedEntity&&gCameraMode==kCameraCockpit));
								if(gConfig->showPlayerNames&&gGameInfo->network&&(drawEntity!=gCarEntities[gReplayViewedEntityID]))
								{
									tVector3 pos=drawEntity->pos+Vector(0,3,0);
									pos=pos-gCameraEntity->pos;
									tMatrix3 inv;
									MatrixTranspose(gCameraEntity->dir,inv);
									pos=pos*inv;
									float size=0.8+pos.z*0.005;
									for(int i=0;i<gGameInfo->numPlayers;i++)
										if(gCarEntities[i]==drawEntity)
											TextPrintfToBufferFormatedVector3(pos,size,kTextAlignMiddle,gGameInfo->playerNames[i]);
								}
							}
							break;
						case kRenderTypeGhost:
							if(!gConfig->noGhost)
								CarRenderEntityGhost(drawEntity);
							break;
					}
				}
		}
		drawEntity=(tGameEntity*)drawEntity->prev;
	}
	
	glPopAttrib();
}

#define kEntMinShadow 6

void RenderShadowPass(tVector3 *clipPlanes,int highQual)
{
	glPushAttrib(GL_ENABLE_BIT+GL_POLYGON_BIT);

	tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->next;
	while(drawEntity!=gFirstEntity&&drawEntity->zDist>-200)
	{
		if(gConfig->gfxDynamics>=0.2||drawEntity==gViewedEntity)
		if(drawEntity->renderType==kRenderTypeModel||drawEntity->renderType==kRenderTypeCar)
			if((sqr(drawEntity->pos-gCameraEntity->pos)<sqr(30))==highQual)
			{
				int shad=true;
				if(drawEntity->renderType==kRenderTypeModel)
					if(drawEntity->physicsType!=kPhysicsTypeSolid)
						shad=false;
					else
					{
						tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData;
						if(ent->shadowModel==kFileErr)	
							shad=false;					
					}
				
				if(shad)
				{
					float shadowLength=GetGroundOffset(drawEntity->pos,&drawEntity->lastRoadIndex,0,0)+kEntMinShadow;
					shadowLength/=gEnvironment->lightDir.y;
					float distance=ModelGetMaxExtends(drawEntity->renderData)+shadowLength;
					if(drawEntity->zDist>-distance&&drawEntity->zDist<ClipDistance()-distance)
						if(!ClipPointDistanced(clipPlanes,&drawEntity->pos,distance))
						{
							SetupTranslation(drawEntity->pos,drawEntity->dir);
							switch(drawEntity->renderType)
							{
								case kRenderTypeCar:
									if(gCameraMode!=kCameraCockpitCarHidden||drawEntity!=gCarEntities[gReplayViewedEntityID])
										CarRenderEntityShadow(drawEntity,0);
									break;
								case kRenderTypeModel:
									tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData;
									DrawModelShadow(ent->shadowModel,shadowLength);
									break;
						}
					}
				}
			}
		drawEntity=(tGameEntity*)drawEntity->next;
	}
	glPopAttrib();
}

#define kLightConeClip 200

void RenderLightConesPass(tVector3 *clipPlanes)
{
	tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->next;
	while(drawEntity!=gFirstEntity&&drawEntity->zDist>-25)
	{
		if(!ClipPointDistanced(clipPlanes,&drawEntity->pos,kLightConeClip))
		{
			switch(drawEntity->renderType)
			{
				case kRenderTypeCar:
					if(drawEntity==gViewedEntity)
					{
						tCarPhysics *phys=(tCarPhysics*)drawEntity->physics;
						tCarDefinition *car=&(phys->car);
						RenderEntityLightCones(drawEntity,car->numLights,phys->lightFlags,car->lights);
					}
					break;
				case kRenderTypeModel:
					if(drawEntity->physicsType==kPhysicsTypeSolid)
					{
						tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData;
						RenderEntityLightCones(drawEntity,ent->numLights,0xffff,ent->lights);
					}
					break;
			}
		}
		drawEntity=(tGameEntity*)drawEntity->next;
	}
}

void RenderLightsPass(tVector3 *clipPlanes)
{
	gTexturesQualityModifier=-255;
	tGameEntity *drawEntity=(tGameEntity*)gFirstEntity->next;
	while(drawEntity!=gFirstEntity)
	{
		SetupTranslation(drawEntity->pos,drawEntity->dir);
		switch(drawEntity->renderType)
		{
			case kRenderTypeCar:{
					tCarPhysics *phys=(tCarPhysics*)drawEntity->physics;
					tCarDefinition *car=&(phys->car);
					RenderEntityLights(drawEntity,car->numLights,phys->lightFlags,phys->echo,car->lights);
				}break;
			case kRenderTypeModel:
				if(drawEntity->physicsType==kPhysicsTypeSolid)
				{
					int objFrameCount=gFrameCount+10*((drawEntity->id*31)%17);
					int allLightEnable=gEnvironment->spotLightEnable;
					if(gMapEnv)
						if(gMapEnv->lightEnable)
							allLightEnable=true;
					int objLightFlags=0x1
						|(((int)(objFrameCount/(2*kFPS))%2)?0x2:0)
						|(((int)(objFrameCount/(1*kFPS))%2)?0x4:0)
						|(((int)(objFrameCount/(0.5*kFPS))%2)?0x8:0)
						|(((int)(objFrameCount/(0.3*kFPS))%4)?0:0x10)
						|(allLightEnable?0x20:0);
					tSolidEntityPhysics *ent=(tSolidEntityPhysics*)gFileTable[drawEntity->physicsData].parsedData;
					RenderEntityLights(drawEntity,ent->numLights,objLightFlags,0,ent->lights);
				}
				break;
		}
		drawEntity=(tGameEntity*)drawEntity->next;
	}
	gTexturesQualityModifier=0;
}


void SetupLighting()
{	
	if(gCameraEntity)
		SetupWorldTranslation();
	
	GLfloat globalLightAmbient[4];	
	*(tVector3*)globalLightAmbient=gEnvironment->ambient;
	globalLightAmbient[3]=1;

	GLfloat globalLightDiffuse[4];	
	*(tVector3*)globalLightDiffuse=gEnvironment->diffuse;
	globalLightDiffuse[3]=1;

	GLfloat globalLightSpecular[4];	
	*(tVector3*)globalLightSpecular=gEnvironment->specular;
	globalLightSpecular[3]=1;

	GLfloat globalLightPosition[4];	
	*(tVector3*)globalLightPosition=gEnvironment->lightDir;
	if(gMapEnv)
		if(!VectorZero(gMapEnv->lightDir))
			*(tVector3*)globalLightPosition=gMapEnv->lightDir;

	globalLightPosition[3]=0;
	
	if(gLightning)
	{
		globalLightAmbient[0]=1;
		globalLightAmbient[1]=1;
		globalLightAmbient[2]=1;
		globalLightDiffuse[0]=1;
		globalLightDiffuse[1]=1;
		globalLightDiffuse[2]=1;
		globalLightSpecular[0]=1;
		globalLightSpecular[1]=1;
		globalLightSpecular[2]=1;
	}
	
	glEnable(GL_LIGHT0);		
	glLightfv(GL_LIGHT0,GL_AMBIENT,globalLightAmbient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,globalLightDiffuse);
	glLightfv(GL_LIGHT0,GL_SPECULAR,globalLightSpecular);
	glLightfv(GL_LIGHT0,GL_POSITION,globalLightPosition);
	
	if(gMapEnv)	
		if(gMapEnv->fogBegin)
		{
			GLfloat fogColor[4];	
			*(tVector3*)fogColor=gMapEnv->fogColor;
			fogColor[3]=1;
			glFogfv(GL_FOG_COLOR,fogColor);
			glFogf(GL_FOG_START,gMapEnv->fogBegin*(1+0.3*gMapEnv->fogOscillation*sin(gFrameCount*kFrameTime*0.7*gMapEnv->fogOscillationSpeed)));	
			glFogf(GL_FOG_END,gMapEnv->fogEnd*(1+0.5*gMapEnv->fogOscillation*sin(gFrameCount*kFrameTime*0.3*gMapEnv->fogOscillationSpeed)));		
		}	
}

void DrawFlare(tFileRef texture,float x,float y,float size,float alpha)
{
	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_ONE,GL_ONE);

	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-1.0f);

	glTranslatef(x,y,0);
	glScalef(size*0.5,size*0.5,0);
	
	glColor4f(1,1,1,alpha);
	
	TexturesSelectTex(texture);
	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();
	
	glPopAttrib();
}

void FlaresDraw1()
{
	if(gEnvironment->flaresEnable)
	{
		tMatrix3 cameraInverse;
		MatrixTranspose(gCameraEntity->dir,cameraInverse);
		tVector3 flarePos=gEnvironment->flareDir*cameraInverse;
		if(gMapEnv)
			if(!VectorZero(gMapEnv->flareDir))
				flarePos=gMapEnv->flareDir*cameraInverse;

		if(flarePos.z<=0)return;
		tVector2 flarePosProjected=Vector(flarePos.x/flarePos.z,-flarePos.y/flarePos.z);
			
		DrawFlare(FileGetReference("flare1.tif"),-flarePosProjected.x*0.5,-flarePosProjected.y*0.5,0.6,1.0);
	}
}

void FlaresDraw2()
{
	if(gEnvironment->flaresEnable)
	{
		tMatrix3 cameraInverse;
		MatrixTranspose(gCameraEntity->dir,cameraInverse);
		tVector3 flarePos=gEnvironment->flareDir*cameraInverse;
		if(gMapEnv)
			if(!VectorZero(gMapEnv->flareDir))
				flarePos=gMapEnv->flareDir*cameraInverse;

		if(flarePos.z<=0)return;
		tVector2 flarePosProjected=Vector(flarePos.x/flarePos.z,-flarePos.y/flarePos.z);
			
		DrawFlare(FileGetReference("flare5.tif"),flarePosProjected.x*1.5,flarePosProjected.y*1.5,0.6,1.0);
		DrawFlare(FileGetReference("flare4.tif"),flarePosProjected.x*0.5,flarePosProjected.y*0.5,0.6,1.0);
		DrawFlare(FileGetReference("flare3.tif"),flarePosProjected.x*0.25,flarePosProjected.y*0.25,0.6,1.0);
		DrawFlare(FileGetReference("flare2.tif"),-flarePosProjected.x*0.1,-flarePosProjected.y*0.1,0.6,1.0);
	}
}

void GameShowInfo();

void RenderVisWalls()
{
	SetupWorldTranslation();
	
	glColorMask(0,0,0,0);
	glDisable(GL_CULL_FACE);
	for(int i=0;i<gMapInfo->numVisWalls;i++)
	{
		glBegin(GL_TRIANGLES);
		glVertex3fv(&(gMapInfo->visWalls[i].a.x));
		glVertex3fv(&(gMapInfo->visWalls[i].b.x));
		glVertex3fv(&(gMapInfo->visWalls[i].c.x));

		glVertex3fv(&(gMapInfo->visWalls[i].b.x));
		glVertex3fv(&(gMapInfo->visWalls[i].c.x));
		glVertex3fv(&((gMapInfo->visWalls[i].c+(gMapInfo->visWalls[i].b-gMapInfo->visWalls[i].a)).x));
		glEnd();
	}
	glEnable(GL_CULL_FACE);
	glColorMask(1,1,1,1);
}

int gLastFrameBlur=false;
int gLastGraphFrame=0;

#define kMaxBlur 0.925
void MotionBlur()
{
	float blur;
	if(gConfig->carsOnSpeed)
		//blur=0.9+0.25*sin(gFrameCount*kFrameTime*0.2);
		blur=1;
	else 
	{
		if(!gConfig->motionBlur)
			return;
		blur=((~gCameraEntity->velo-30)/55.0);
	}
	int blurExp=gFrameCount-gLastGraphFrame-1;
	if(blurExp>10)blurExp=10;
	if(blurExp<0)blurExp=0;
	blur*=pow(kMaxBlur,blurExp);
	if(blur>0&&(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo||gConfig->carsOnSpeed)&&gConfig->motionBlur)
	{
		if(blur>1)
			blur=1;
		glPushAttrib(GL_ENABLE_BIT+GL_CURRENT_BIT);
		glDisable(GL_CULL_FACE);
		glDisable(GL_LIGHTING);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glLoadIdentity();
		glColor4f(1,1,1,kMaxBlur*blur);
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glOrtho(0,gConfig->screenXSize,0,gConfig->screenYSize,-1,2);
		for(int i=0;i<4;i++)
			if(gScreenTextures[i].xSize)
			{
				glBindTexture(GL_TEXTURE_2D,gScreenTextures[i].texture);
				if(gLastFrameBlur)
				{
					glBegin(GL_TRIANGLE_STRIP);	
					glTexCoord2d(1,1); glVertex3f(gScreenTextures[i].xSize+gScreenTextures[i].xOffset,	gScreenTextures[i].ySize+gScreenTextures[i].yOffset,1);		
					glTexCoord2d(1,0); glVertex3f(gScreenTextures[i].xSize+gScreenTextures[i].xOffset,	gScreenTextures[i].yOffset,1); 	
					glTexCoord2d(0,1); glVertex3f(0+gScreenTextures[i].xOffset,							gScreenTextures[i].ySize+gScreenTextures[i].yOffset,1); 	
					glTexCoord2d(0,0); glVertex3f(0+gScreenTextures[i].xOffset,							gScreenTextures[i].yOffset,1);
					glEnd();
				}
				glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,gScreenTextures[i].xOffset,gScreenTextures[i].yOffset,gScreenTextures[i].xSize,gScreenTextures[i].ySize,0);
			}
		glPopMatrix();
		glPopAttrib();
		glMatrixMode(GL_MODELVIEW);
		gLastFrameBlur=true;
	}
	else
		gLastFrameBlur=false;
}

void RenderFrame(int screenUpdate)
{
	#ifdef __POLYCOUNT
	gPolyCount=0;
	#endif
	//gClipEnable=false;
	
//	SetupAspect(kNormalFOVY+~gCameraEntity->velo*0.002*gGameInfo->arcade);
	float fov=1.0;
	if(gGameInfo->arcade==kGameModeArcade||gGameInfo->arcade==kGameModeTurbo)
		fov+=~gCameraEntity->velo*0.0015*(gGameInfo->arcade+1);
	if(fov>1.6)fov=1.6;
	SetupAspect(fov);
	
	tVector3 clipPlanes[kNumClipPlanes];
	SetupClipPlanes(gCameraEntity,clipPlanes);

	SetupLighting();
	
	SkyRender();
	FlaresDraw1();
	RenderVisWalls();
	
	SortEntities();
	
	RenderObjectPass(clipPlanes); //first pass: objects

	if(!gMapInfo->dontDrawRoad)
		RoadRender(clipPlanes);
	TracksRender(clipPlanes);
	
	if(gConfig->stencil)//second pass: shadows/light Cones 
	{	
		int stencil=4.0f*gConfig->gfxDynamics;
		if(stencil<=0)
			stencil=1;
		if(gEnvironment->shadowEnable)
		{
			for(int i=1;i<=stencil;i++)
			{
				gStencilZoom=0.9+0.1*i/(float)(stencil);
				RenderShadowPass(clipPlanes,true);
				RenderStencilLayer(true,stencil);
			}
			gStencilZoom=1.0;
			RenderShadowPass(clipPlanes,false);
			RenderStencilLayer(true,1);
		}
		if(gEnvironment->spotLightEnable)
		{
			for(int i=1;i<=stencil*1.5;i++)
			{
				gStencilZoom=0.75+0.25*i/((float)stencil*1.5);
				RenderLightConesPass(clipPlanes);
				RenderStencilLayer(false,stencil);
			}
		}
	}	
	DrawTransparentPolys(clipPlanes);//third pass: transparent stuff
	RenderLightsPass(clipPlanes); //fourth pass: lights
	
	ParticlesDraw();

		
	SetupAspect(kNormalFOVY);
	FlaresDraw2();

	if(!gBackgroundReplay)
	{
		GameShowInfo();
		MotionBlur();
		if(screenUpdate)
			ScreenBlit();
	}
	gLastGraphFrame=gFrameCount;
}