/*
   Copyright (C) 2002-2003 Victor Luchits

   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.

 */

#include "cg_local.h"

// cg_view.c -- player rendering positioning

//======================================================================
//					ChaseHack (In Eyes Chasecam)
//======================================================================

cg_chasecam_t chaseCam;

int CG_LostMultiviewPOV( void );

//===============
//CG_ChaseStep
//===============
static void CG_ChaseStep( int step )
{
	if( chaseCam.mode < 0 || chaseCam.mode >= CAM_MODES )
		return;

	if( cg.frame.multipov )
	{
		int i, checkPlayer;
		int curPlayer;

		// find the playerState containing our current POV, then cycle playerStates
		curPlayer = -1;
		for( i = 0; i < cg.frame.numplayers; i++ )
		{
			if( cg.frame.playerStates[i].POVnum > 0 && cg.frame.playerStates[i].POVnum == cg.multiviewPOVnum )
			{
				curPlayer = i;
				break;
			}
		}

		// the POV was lost, find the closer one (may go up or down, but who cares)
		if( curPlayer == -1 )
		{
			curPlayer = CG_LostMultiviewPOV();
			cg.multiviewPOVnum = cg.frame.playerStates[curPlayer].POVnum;
		}
		else
		{
			checkPlayer = curPlayer;
			for( i = 0; i < cg.frame.numplayers; i++ )
			{
				checkPlayer += step;
				if( checkPlayer < 0 )
					checkPlayer = cg.frame.numplayers - 1;
				else if( checkPlayer >= cg.frame.numplayers )
					checkPlayer = 0;

				if( ( checkPlayer != curPlayer ) && cg.frame.playerStates[checkPlayer].stats[STAT_REALTEAM] == TEAM_SPECTATOR )
					continue;
				break;
			}

			cg.multiviewPOVnum = cg.frame.playerStates[checkPlayer].POVnum;
		}
	}
	else if( !cgs.demoPlaying )
	{
		trap_Cmd_ExecuteText( EXEC_NOW, (step < 0 ? "chaseprev" : "chasenext") );
	}
}

//===============
//CG_ChasePrev
//===============
void CG_ChasePrev( void )
{
	CG_ChaseStep( -1 );
}

//===============
//CG_ChaseNext
//===============
void CG_ChaseNext( void )
{
	CG_ChaseStep( 1 );
}

//===================================================================

//==============
//CG_AddLocalSounds
//==============
static void CG_AddLocalSounds( void )
{
	static qboolean postmatchsound_set = qfalse, demostream = qfalse;

	// add sounds from announcer
	CG_ReleaseAnnouncerEvents();

	// if in postmatch, play postmatch song
	if( cg.frame.match.state >= MATCH_STATE_POSTMATCH )
	{
		if( !postmatchsound_set && !demostream )
		{
			trap_S_StopBackgroundTrack();
			trap_S_StartBackgroundTrack( va( S_MUSIC_POSTMATCH_1_to_7, (int)brandom( 1, 7 ) ), NULL );
			postmatchsound_set = qtrue;
		}
	}
	else
	{
		if( cgs.demoPlaying && cgs.demoAudioStream && !demostream )
		{
			trap_S_StopBackgroundTrack();
			trap_S_StartBackgroundTrack( cgs.demoAudioStream, NULL );
			demostream = qtrue;
		}

		if( postmatchsound_set )
		{
			trap_S_StopBackgroundTrack();
			postmatchsound_set = qfalse;
		}
		// ctf flag sounds
		if( cg.frame.playerState.stats[STAT_GAMETYPE] == GAMETYPE_CTF )
		{
			// TODO: Add new CTF sounds?? (KoFFiE)
			/*
			   static int flagNextBipTimer = 100;
			   static int lastBipTime;
			   centity_t *cent = &cg_entities[cg.view.POVent];

			   if( cg.playerState.stats[STAT_CTF_FLAG_BLEEP] == STAT_NOTSET ||
			    !(cent->current.effects & EF_ENEMY_FLAG) ) // ignore if not a flag carrier
			   {
			    lastBipTime = STAT_NOTSET;
			   }
			   else { // the timer is up
			    flagNextBipTimer -= cg.frameTime * 1000;
			    if( flagNextBipTimer <= 0 ) {
			   	int curBipTime;

			   	curBipTime = cg.playerState.stats[STAT_CTF_FLAG_BLEEP];

			   	flagNextBipTimer = 1000;

			   	if( lastBipTime == STAT_NOTSET || lastBipTime > curBipTime + 1 ) {// counting down
			   	    trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxTimerBipBip ), CHAN_AUTO, 0.5f );
			   	    flagNextBipTimer = 1000;
			   	}
			   	else { // counting up
			   	    trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxTimerPloink ), CHAN_AUTO, 0.5f );
			   	    flagNextBipTimer = 2000;
			   	}

			   	lastBipTime = curBipTime;
			    }
			   }
			 */
		}
	}
}

//===============
//CG_SetSensitivityScale
//Scale sensitivity for different view effects
//===============
float CG_SetSensitivityScale( const float sens )
{
	float sensScale = 1.0f;

	if( sens && ( cg.frame.playerState.stats[STAT_LAYOUTS] & STAT_LAYOUT_ZOOM ) )
	{
		if( cg_zoomSens->value )
		{
			return cg_zoomSens->value/sens;
		}
		else if( cg_fov->value )
		{
			return ( cg.frame.playerState.fov / cg_fov->value ); // FIXME: cg_fov might not be the actual fov?
		}
	}

	return sensScale;
}

//===================================================================

//==============
//CG_AddKickAngles
//==============
void CG_AddKickAngles( vec3_t viewangles )
{
	float time;
	float uptime;
	float delta;
	int i;

	for( i = 0; i < MAX_ANGLES_KICKS; i++ )
	{
		if( cg.time > cg.kickangles[i].timestamp + cg.kickangles[i].kicktime )
			continue;

		time = (float)( ( cg.kickangles[i].timestamp + cg.kickangles[i].kicktime ) - cg.time );
		uptime = ( (float)cg.kickangles[i].kicktime ) * 0.5f;
		delta = 1.0f - ( abs( time - uptime ) / uptime );
		//CG_Printf("Kick Delta:%f\n", delta );
		if( delta > 1.0f )
			delta = 1.0f;
		if( delta <= 0.0f )
			continue;

		viewangles[PITCH] += cg.kickangles[i].v_pitch * delta;
		viewangles[ROLL] += cg.kickangles[i].v_roll * delta;
	}
}

//================
//CG_CalcViewBob
//================
static void CG_CalcViewBob( void )
{
	float bobMove, bobTime, bobScale;

	if( !cg.view.drawWeapon )
		return;

	// calculate speed and cycle to be used for all cyclic walking effects
	cg.xyspeed = sqrt( cg.predictedVelocity[0]*cg.predictedVelocity[0] + cg.predictedVelocity[1]*cg.predictedVelocity[1] );

	bobScale = 0;
	if( cg.xyspeed < 5 )
		cg.oldBobTime = 0;  // start at beginning of cycle again
	else if( cg_gunbob->integer )
	{
		if( !ISVIEWERENTITY( cg.view.POVent ) )
			bobScale = 0.0f;
		else if( CG_PointContents( cg.view.refdef.vieworg ) & MASK_WATER )
			bobScale =  0.75f;
		else
		{
			centity_t *cent;
			vec3_t mins, maxs;
			trace_t	trace;

			cent = &cg_entities[cg.view.POVent];
			GS_BBoxForEntityState( &cent->current, mins, maxs );
			maxs[2] = mins[2];
			mins[2] -= ( 1.6f*STEPSIZE );
			CG_Trace( &trace, cg.predictedOrigin, mins, maxs, cg.predictedOrigin, cg.view.POVent, MASK_PLAYERSOLID );
			if( trace.startsolid || trace.allsolid )
			{
				if( cg.frame.playerState.pmove.pm_flags & PMF_DUCKED )
					bobScale = 1.5f;
				else
					bobScale = 2.5f;
			}
		}
	}

	bobMove = cg.frameTime * bobScale;
	bobTime = ( cg.oldBobTime += bobMove );

	cg.bobCycle = (int)bobTime;
	cg.bobFracSin = fabs( sin( bobTime*M_PI ) );
}

//============================================================================

//==================
//CG_SkyPortal
//==================
int CG_SkyPortal( void )
{
	float fov = 0;
	float scale = 0;
	float yawspeed = 0;
	skyportal_t *sp = &cg.view.refdef.skyportal;

	if( cgs.configStrings[CS_SKYBOX][0] == '\0' )
		return 0;

	if( sscanf( cgs.configStrings[CS_SKYBOX], "%f %f %f %f %f %f",
		&sp->vieworg[0], &sp->vieworg[1], &sp->vieworg[2], &fov, &scale, &yawspeed ) >= 3 )
	{
		sp->fov = fov;
		sp->scale = scale ? 1.0f / scale : 0;
		VectorSet( sp->viewanglesOffset, 0, cg.view.refdef.time * 0.001f * yawspeed, 0 );
		return RDF_SKYPORTALINVIEW;
	}

	return 0;
}

//==================
//CG_RenderFlags
//==================
static int CG_RenderFlags( void )
{
	int rdflags, contents;

	rdflags = 0;

	contents = CG_PointContents( cg.view.refdef.vieworg );
	if( contents & MASK_WATER )
		rdflags |= RDF_UNDERWATER;
	else
		rdflags &= ~RDF_UNDERWATER;

	if( cg.oldAreabits )
		rdflags |= RDF_OLDAREABITS;

	if( cg.portalInView )
		rdflags |= RDF_PORTALINVIEW;

	if( cg_outlineWorld->integer )
		rdflags |= RDF_WORLDOUTLINES;

	rdflags |= RDF_BLOOM;
	rdflags |= CG_SkyPortal();

	return rdflags;
}

//===============
//CG_InterpolatePlayerState
//===============
static void CG_InterpolatePlayerState( player_state_t *playerState )
{
	int i;
	player_state_t *ps, *ops;

	ps = &cg.frame.playerState;
	ops = &cg.oldFrame.playerState;

	*playerState = *ps;

	// if the player entity was teleported this frame use the final position
	if( abs( ops->pmove.origin[0] - ps->pmove.origin[0] ) > 256
	    || abs( ops->pmove.origin[1] - ps->pmove.origin[1] ) > 256
	    || abs( ops->pmove.origin[2] - ps->pmove.origin[2] ) > 256 )
	{
		VectorCopy( ps->pmove.origin, playerState->pmove.origin );
		VectorCopy( ps->viewangles, playerState->viewangles );
	}
	else
	{
		for( i = 0; i < 3; i++ )
		{
			playerState->pmove.origin[i] = ops->pmove.origin[i] + cg.lerpfrac * ( ps->pmove.origin[i] - ops->pmove.origin[i] );
			playerState->viewangles[i] = LerpAngle( ops->viewangles[i], ps->viewangles[i], cg.lerpfrac );
		}
	}

	// always interpolate fov and viewheight
	playerState->fov = ops->fov + cg.lerpfrac * ( ps->fov - ops->fov );
	playerState->viewheight = ops->viewheight + cg.lerpfrac * ( ps->viewheight - ops->viewheight );
}

//=================
//CG_ThirdPersonOffsetView
//=================
static void CG_ThirdPersonOffsetView( cg_viewdef_t *view )
{
	float dist, f, r;
	vec3_t dest, stop;
	vec3_t chase_dest;
	trace_t	trace;
	vec3_t mins = { -4, -4, -4 };
	vec3_t maxs = { 4, 4, 4 };

	if( !cg_thirdPersonAngle || !cg_thirdPersonRange )
	{
		cg_thirdPersonAngle = trap_Cvar_Get( "cg_thirdPersonAngle", "0", CVAR_ARCHIVE );
		cg_thirdPersonRange = trap_Cvar_Get( "cg_thirdPersonRange", "70", CVAR_ARCHIVE );
	}

	// calc exact destination
	VectorCopy( view->refdef.vieworg, chase_dest );
	r = DEG2RAD( cg_thirdPersonAngle->value );
	f = -cos( r );
	r = -sin( r );
	VectorMA( chase_dest, cg_thirdPersonRange->value * f, view->axis[FORWARD], chase_dest );
	VectorMA( chase_dest, cg_thirdPersonRange->value * r, view->axis[RIGHT], chase_dest );
	chase_dest[2] += 8;

	// find the spot the player is looking at
	VectorMA( view->refdef.vieworg, 512, view->axis[FORWARD], dest );
	GS_Trace( &trace, view->refdef.vieworg, mins, maxs, dest, view->POVent, MASK_SOLID );

	// calculate pitch to look at the same spot from camera
	VectorSubtract( trace.endpos, view->refdef.vieworg, stop );
	dist = sqrt( stop[0] * stop[0] + stop[1] * stop[1] );
	if( dist < 1 )
		dist = 1;
	view->refdef.viewangles[PITCH] = RAD2DEG( -atan2( stop[2], dist ) );
	view->refdef.viewangles[YAW] -= cg_thirdPersonAngle->value;
	AngleVectors( view->refdef.viewangles, view->axis[FORWARD], view->axis[RIGHT], view->axis[UP] );

	// move towards destination
	GS_Trace( &trace, view->refdef.vieworg, mins, maxs, chase_dest, view->POVent, MASK_SOLID );

	if( trace.fraction != 1.0 )
	{
		VectorCopy( trace.endpos, stop );
		stop[2] += ( 1.0 - trace.fraction ) * 32;
		GS_Trace( &trace, view->refdef.vieworg, mins, maxs, stop, view->POVent, MASK_SOLID );
		VectorCopy( trace.endpos, chase_dest );
	}

	VectorCopy( chase_dest, view->refdef.vieworg );
}

//===============
//CG_ViewSmoothPredictedSteps
//===============
static void CG_ViewSmoothPredictedSteps( vec3_t vieworg )
{
	int timeDelta;

	// smooth out stair climbing
	timeDelta = cg.realTime - cg.predictedStepTime;
	if( timeDelta < PREDICTED_STEP_TIME )
	{
		vieworg[2] -= cg.predictedStep * ( PREDICTED_STEP_TIME - timeDelta ) / PREDICTED_STEP_TIME;
	}
}

//===============
//CG_ChaseCamButtons
//===============
static void CG_ChaseCamButtons( void )
{
#define CHASECAMBUTTONSDELAY ( cg.time + 250 )
	usercmd_t cmd;

	// Vic: FIXME! Clean up this code!
	if( cgs.demoPlaying || cg.frame.multipov )
	{
		trap_NET_GetUserCmd( trap_NET_GetCurrentUserCmdNum() - 1, &cmd );
		if( cg.time > chaseCam.cmd_mode_delay )
		{
			if( ( cmd.buttons & BUTTON_ATTACK ) )
			{
				chaseCam.mode = ( chaseCam.mode != CAM_THIRDPERSON );
				chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
			}

			if( cg.frame.multipov )
			{
				if( cmd.upmove > 0 || cmd.buttons & BUTTON_SPECIAL )
				{
					CG_ChaseStep( 1 );
					chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
				}

				if( cmd.upmove < 0 )
				{
					CG_ChaseStep( -1 );
					chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
				}
			}
		}
	}
	else if( ( cg.frame.playerState.pmove.pm_type == PM_CHASECAM ) && ( cg.frame.playerState.POVnum != (unsigned)( cgs.playerNum + 1 ) ) )
	{
		trap_NET_GetUserCmd( trap_NET_GetCurrentUserCmdNum() - 1, &cmd );
		if( cg.time > chaseCam.cmd_mode_delay )
		{
			if( ( cmd.buttons & BUTTON_ATTACK ) )
			{
				if( cg.frame.playerState.stats[STAT_REALTEAM] == TEAM_SPECTATOR )
				{
					chaseCam.mode++;
					if( chaseCam.mode >= CAM_MODES )
					{   // if exceeds the cycle, start free fly
						trap_Cmd_ExecuteText( EXEC_NOW, "camswitch" );
						chaseCam.mode = 0; // smallest, to start the new cycle
					}
					chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
				}
			}

			if( cmd.upmove > 0 || cmd.buttons & BUTTON_SPECIAL )
			{
				CG_ChaseStep( 1 );
				chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
			}

			if( cmd.upmove < 0 )
			{
				CG_ChaseStep( -1 );
				chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
			}
		}
	}
	else if( cg.frame.playerState.pmove.pm_type == PM_SPECTATOR )
	{
		chaseCam.mode = CAM_INEYES;
		trap_NET_GetUserCmd( trap_NET_GetCurrentUserCmdNum() - 1, &cmd );
		if( cg.frame.playerState.stats[STAT_REALTEAM] == TEAM_SPECTATOR )
		{
			if( ( cmd.buttons & BUTTON_ATTACK ) && cg.time > chaseCam.cmd_mode_delay )
			{
				trap_Cmd_ExecuteText( EXEC_NOW, "camswitch" );
				chaseCam.cmd_mode_delay = CHASECAMBUTTONSDELAY;
			}
		}
	}
	else
	{
		chaseCam.mode = CAM_INEYES;
	}
#undef CHASECAMBUTTONSDELAY
}

void CG_SetupViewDef( cg_viewdef_t *view, int type )
{
	memset( view, 0, sizeof( cg_viewdef_t ) );

	//
	// VIEW SETTINGS
	//

	view->type = type;

	if( view->type == VIEWDEF_PLAYERVIEW )
	{
		view->POVent = cg.frame.playerState.POVnum;

		view->draw2D = qtrue;

		// set up third-person
		if( cgs.demoPlaying )
			view->thirdperson = CG_DemoCam_GetThirdPerson();
		else if( chaseCam.mode == CAM_THIRDPERSON )
			view->thirdperson = qtrue;
		else
			view->thirdperson = ( cg_thirdPerson->integer != 0 );

		if( cg_entities[view->POVent].serverFrame != cg.frame.serverFrame )
			view->thirdperson = qfalse;

		// check for drawing gun
		if( !view->thirdperson && view->POVent > 0 && view->POVent < MAX_CLIENTS )
		{
			if( ( cg_entities[view->POVent].serverFrame == cg.frame.serverFrame ) &&
				( cg_entities[view->POVent].current.weapon != 0 ) )
				view->drawWeapon = ( cg_gun->integer != 0 );
		}

		// check for chase cams
		if( (unsigned)view->POVent == cgs.playerNum + 1 )
		{
			if( cg_predict->integer && !view->thirdperson && !cgs.demoPlaying && !( cg.frame.playerState.pmove.pm_flags & PMF_NO_PREDICTION ) )
			{
				view->playerPrediction = qtrue;
			}
		}
	}
	else if( view->type == VIEWDEF_CAMERA )
	{
		CG_DemoCam_GetViewDef( view );
	}
	else
	{
		GS_Error( "CG_SetupView: Invalid view type %i\n", view->type );
	}

	//
	// SETUP REFDEF FOR THE VIEW SETTINGS
	//

	if( view->type == VIEWDEF_PLAYERVIEW )
	{
		int i;
		vec3_t viewoffset;

		CG_InterpolatePlayerState( &cg.interpolatedPlayerState );

		VectorSet( viewoffset, 0.0f, 0.0f, cg.interpolatedPlayerState.viewheight );

		if( view->playerPrediction )
		{
			CG_PredictMovement();
			for( i = 0; i < 3; i++ )
			{
				view->refdef.vieworg[i] = cg.predictedOrigin[i] + viewoffset[i] - ( 1.0f - cg.lerpfrac ) * cg.predictionError[i];
				view->refdef.viewangles[i] = cg.predictedAngles[i];
			}
			CG_ViewSmoothPredictedSteps( view->refdef.vieworg ); // smooth out stair climbing
		}
		else
		{
			VectorAdd( cg.interpolatedPlayerState.pmove.origin, viewoffset, view->refdef.vieworg );
			VectorCopy( cg.interpolatedPlayerState.viewangles, view->refdef.viewangles );

			// we didn't run prediction, so copy these values
			VectorCopy( cg.interpolatedPlayerState.pmove.origin, cg.predictedOrigin );
			VectorCopy( cg.interpolatedPlayerState.viewangles, cg.predictedAngles );
			VectorCopy( cg.interpolatedPlayerState.pmove.velocity, cg.predictedVelocity );
		}

		view->refdef.fov_x = cg.interpolatedPlayerState.fov;

		CG_CalcViewBob();
	}
	else if( view->type == VIEWDEF_CAMERA )
	{
		view->refdef.fov_x = CG_DemoCam_GetOrientation( view->refdef.vieworg, view->refdef.viewangles );
	}

	// never let it sit exactly on a node line, because a water plane can
	// disappear when viewed with the eye exactly on it.
	// the server protocol only specifies to 1/16 pixel, so add 1/16 in each axis
	view->refdef.vieworg[0] += 1.0/PM_VECTOR_SNAP;
	view->refdef.vieworg[1] += 1.0/PM_VECTOR_SNAP;
	view->refdef.vieworg[2] += 1.0/PM_VECTOR_SNAP;

	// view rectangle size
	view->refdef.x = scr_vrect.x;
	view->refdef.y = scr_vrect.y;
	view->refdef.width = scr_vrect.width;
	view->refdef.height = scr_vrect.height;
	view->refdef.time = cg.time;
	view->refdef.areabits = cg.frame.areabits;

	view->refdef.fov_y = CalcFov( view->refdef.fov_x, view->refdef.width, view->refdef.height );
	view->fracDistFOV = tan( view->refdef.fov_x * ( M_PI/180 ) * 0.5f );

	AngleVectors( view->refdef.viewangles, view->axis[FORWARD], view->axis[RIGHT], view->axis[UP] );

	if( view->thirdperson )
	{
		CG_ThirdPersonOffsetView( view );
	}
}

/*
   ==================
   CG_RenderView

   ==================
 */
#define	WAVE_AMPLITUDE	0.015   // [0..1]
#define	WAVE_FREQUENCY	0.6     // [0..1]
void CG_RenderView( float frameTime, float realFrameTime, int realTime, unsigned int serverTime, float stereo_separation )
{
	unsigned int prevTime;

	// update time
	cg.realTime = realTime;
	cg.frameTime = frameTime;
	cg.realFrameTime = realFrameTime;
	cg.frameCount++;
	prevTime = cg.time;
	cg.time = serverTime;

	// most likely it's a demojump (time can't go backwards), so clear effects
	if( prevTime > cg.time + cgs.snapFrameTime )
		CG_SoftRestart ();

	if( !cgs.precacheDone || !cg.frame.valid )
	{
		CG_DrawLoading();
		return;
	}

	if( cg.oldFrame.serverTime == cg.frame.serverTime )
	{
		cg.lerpfrac = 1.0f;
	}
	else
	{
		cg.lerpfrac = (double)( cg.time - cg.oldFrame.serverTime ) / (double)( cg.frame.serverTime - cg.oldFrame.serverTime );
	}

	if( cg_showClamp->integer )
	{
		if( cg.lerpfrac > 1.0f )
			CG_Printf( "high clamp %f\n", cg.lerpfrac );
		else if( cg.lerpfrac < 0.0f )
			CG_Printf( "low clamp  %f\n", cg.lerpfrac );
	}

	clamp( cg.lerpfrac, 0.0f, 1.0f );

	if( !cgs.configStrings[CS_MODELS+1][0] )
	{
		trap_R_DrawStretchPic( 0, 0, cgs.vidWidth, cgs.vidHeight, 0, 0, 1, 1, colorBlack, cgs.shaderWhite );
		return;
	}

	CG_CalcVrect(); // find sizes of the 3d drawing screen
	CG_TileClear(); // clear any dirty part of the background

	trap_R_ClearScene();

	CG_RunLightStyles();

	CG_ChaseCamButtons();

	if( CG_DemoCam_Update() )
		CG_SetupViewDef( &cg.view, CG_DemoCam_GetViewType() );
	else
		CG_SetupViewDef( &cg.view, VIEWDEF_PLAYERVIEW );

	CG_LerpEntities();  // interpolate packet entities positions

	CG_CalcViewWeapon();

	// build a refresh entity list
	CG_AddEntities();

	CG_AddLightStyles();

#ifndef WSW_RELEASE
	CG_AddTest();
#endif

	// offset vieworg appropriately if we're doing stereo separation
	if( stereo_separation != 0 )
		VectorMA( cg.view.refdef.vieworg, stereo_separation, cg.view.axis[RIGHT], cg.view.refdef.vieworg );

	cg.view.refdef.rdflags = CG_RenderFlags();

	// warp if underwater
	if( cg.view.refdef.rdflags & RDF_UNDERWATER )
	{
		float phase = cg.view.refdef.time * 0.001 * WAVE_FREQUENCY * M_TWOPI;
		float v = WAVE_AMPLITUDE * ( sin( phase ) - 1.0 ) + 1;
		cg.view.refdef.fov_x *= v;
		cg.view.refdef.fov_y *= v;
	}

	CG_AddLocalSounds();
	CG_SetSceneTeamColors(); // update the team colors in the renderer

	trap_R_RenderScene( &cg.view.refdef );

	cg.oldAreabits = qtrue;

	// update audio
	// Medar: TODO: velocity
	trap_S_Update( cg.view.refdef.vieworg, vec3_origin, cg.view.axis[FORWARD], cg.view.axis[RIGHT], cg.view.axis[UP] );

	CG_UpdateDamageIndicatorValues();

	CG_Draw2D();

	CG_ResetTemporaryBoneposesCache(); // skelmod : reset for next frame
}
