//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================

// view/refresh setup functions

#include "hud.h"
#include "cl_util.h"
#include "cvardef.h"
#include "usercmd.h"
#include "const.h"

#include "entity_state.h"
#include "cl_entity.h"
#include "ref_params.h"
#include "in_defs.h" // PITCH YAW ROLL
#include "pm_movevars.h"
#include "pm_shared.h"
#include "pm_defs.h"
#include "event_api.h"
#include "pmtrace.h"
#include "screenfade.h"
#include "shake.h"
#include "mod/AvHClientUtil.h"
#include "APIProxy.h"
#include "Exports.h"
#include "hltv.h"
#include "util/MathUtil.h"
#include "util/STLUtil.h"
#include "mod/AvHMarineEquipmentConstants.h"
#include "mod/AvHMarineWeaponConstants.h"
#include "mod/AvHAlienWeaponConstants.h"
#include "mod/AvHSpecials.h"

extern		float	gTopDownViewOrigin[3];
extern		float	gTopDownViewAngles[3];

#ifndef M_PI
#define M_PI		3.14159265358979323846	// matches value in gcc v2 math.h
#endif

void PM_ParticleLine( float *start, float *end, int pcolor, float life, float vert);
int PM_GetVisEntInfo( int ent );
int PM_GetPhysEntInfo( int ent );
void InterpolateAngles( float *start, float *end, float *output, float frac );
void NormalizeAngles(float* angles);
float AngleBetweenVectors(float * v1, float * v2 );

void V_DropPunchAngle ( float frametime, float *ev_punchangle );
void VectorAngles( const float *forward, float *angles );
void V_CalcTopDownRefdef ( struct ref_params_s *pparams );

extern float	vJumpOrigin[3];
extern float	vJumpAngles[3];

#include "r_studioint.h"
#include "com_model.h"

extern engine_studio_api_t IEngineStudio;

/*
The view is allowed to move slightly from it's true position for bobbing,
but if it exceeds 8 pixels linear distance (spherical, not box), the list of
entities sent from the server may not include everything in the pvs, especially
when crossing a water boudnary.
*/

extern cvar_t	*chase_active;
extern cvar_t	*scr_ofsx, *scr_ofsy, *scr_ofsz;
extern cvar_t	*cl_vsmoothing;

#define	CAM_MODE_RELAX		1
#define CAM_MODE_FOCUS		2

vec3_t		v_origin, v_angles, v_cl_angles, v_sim_org, v_lastAngles, v_view_ofs;
float		v_frametime, v_lastDistance;	
float		v_cameraRelaxAngle	= 5.0f;
float		v_cameraFocusAngle	= 35.0f;
int			v_cameraMode = CAM_MODE_FOCUS;
qboolean	v_resetCamera = 1;

extern float	gTopDownHeight;

vec3_t gLastCommanderViewpoint;

vec3_t ev_punchangle;

cvar_t	*scr_ofsx;
cvar_t	*scr_ofsy;
cvar_t	*scr_ofsz;

cvar_t	*v_centermove;
cvar_t	*v_centerspeed;

cvar_t	*cl_bobcycle;
cvar_t	*cl_bob;
cvar_t	*cl_bobup;
cvar_t	*cl_waterdist;
cvar_t	*cl_chasedist;
cvar_t  *cl_hudcam;

// These cvars are not registered (so users can't cheat), so set the ->value field directly
// Register these cvars in V_Init() if needed for easy tweaking
cvar_t	v_iyaw_cycle		= {"v_iyaw_cycle", "2", 0, 2};
cvar_t	v_iroll_cycle		= {"v_iroll_cycle", "0.5", 0, 0.5};
cvar_t	v_ipitch_cycle		= {"v_ipitch_cycle", "1", 0, 1};
cvar_t	v_iyaw_level		= {"v_iyaw_level", "0.3", 0, 0.3};
cvar_t	v_iroll_level		= {"v_iroll_level", "0.1", 0, 0.1};
cvar_t	v_ipitch_level		= {"v_ipitch_level", "0.3", 0, 0.3};

float	v_idlescale;  // used by TFC for concussion grenade effect

/*
//=============================================================================
void V_NormalizeAngles( float *angles )
{
	int i;
	// Normalize angles
	for ( i = 0; i < 3; i++ )
	{
		if ( angles[i] > 180.0 )
		{
			angles[i] -= 360.0;
		}
		else if ( angles[i] < -180.0 )
		{
			angles[i] += 360.0;
		}
	}
}

/*
===================
V_InterpolateAngles

Interpolate Euler angles.
FIXME:  Use Quaternions to avoid discontinuities
Frac is 0.0 to 1.0 ( i.e., should probably be clamped, but doesn't have to be )
===================

void V_InterpolateAngles( float *start, float *end, float *output, float frac )
{
	int i;
	float ang1, ang2;
	float d;
	
	V_NormalizeAngles( start );
	V_NormalizeAngles( end );

	for ( i = 0 ; i < 3 ; i++ )
	{
		ang1 = start[i];
		ang2 = end[i];

		d = ang2 - ang1;
		if ( d > 180 )
		{
			d -= 360;
		}
		else if ( d < -180 )
		{	
			d += 360;
		}

		output[i] = ang1 + d * frac;
	}

	V_NormalizeAngles( output );
} */

// Quakeworld bob code, this fixes jitters in the mutliplayer since the clock (pparams->time) isn't quite linear
float V_CalcBob ( struct ref_params_s *pparams )
{
	static	double	bobtime;
	static float	bob;
	float	cycle;
	static float	lasttime;
	vec3_t	vel;
	
	if ( pparams->onground == -1 ||
		 pparams->time == lasttime )
	{
		// just use old value
		return bob;	
	}

	lasttime = pparams->time;

	bobtime += pparams->frametime;
	cycle = bobtime - (int)( bobtime / cl_bobcycle->value ) * cl_bobcycle->value;
	cycle /= cl_bobcycle->value;
	
	if ( cycle < cl_bobup->value )
	{
		cycle = M_PI * cycle / cl_bobup->value;
	}
	else
	{
		cycle = M_PI + M_PI * ( cycle - cl_bobup->value )/( 1.0 - cl_bobup->value );
	}

	// bob is proportional to simulated velocity in the xy plane
	// (don't count Z, or jumping messes it up)
	VectorCopy( pparams->simvel, vel );
	vel[2] = 0;

	bob = sqrt( vel[0] * vel[0] + vel[1] * vel[1] ) * cl_bob->value;
	bob = bob * 0.3 + bob * 0.7 * sin(cycle);
	bob = min( bob, 4 );
	bob = max( bob, -7 );
	return bob;
	
}

/*
===============
V_CalcRoll
Used by view and sv_user
===============
*/
float V_CalcRoll (vec3_t angles, vec3_t velocity, float rollangle, float rollspeed )
{
    float   sign;
    float   side;
    float   value;
	vec3_t  forward, right, up;
    
	AngleVectors ( angles, forward, right, up );
    
	side = DotProduct (velocity, right);
    sign = side < 0 ? -1 : 1;
    side = fabs( side );
    
	value = rollangle;
    if (side < rollspeed)
	{
		side = side * value / rollspeed;
	}
    else
	{
		side = value;
	}
	return side * sign;
}

typedef struct pitchdrift_s
{
	float		pitchvel;
	int			nodrift;
	float		driftmove;
	double		laststop;
} pitchdrift_t;

static pitchdrift_t pd;

void V_StartPitchDrift( void )
{
	if ( pd.laststop == gEngfuncs.GetClientTime() )
	{
		return;		// something else is keeping it from drifting
	}

	if ( pd.nodrift || !pd.pitchvel )
	{
		pd.pitchvel = v_centerspeed->value;
		pd.nodrift = 0;
		pd.driftmove = 0;
	}
}

void V_StopPitchDrift ( void )
{
	pd.laststop = gEngfuncs.GetClientTime();
	pd.nodrift = 1;
	pd.pitchvel = 0;
}

/*
===============
V_DriftPitch

Moves the client pitch angle towards idealpitch sent by the server.

If the user is adjusting pitch manually, either with lookup/lookdown,
mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
===============
*/
void V_DriftPitch ( struct ref_params_s *pparams )
{
	float		delta, move;

	if ( gEngfuncs.IsNoClipping() || !pparams->onground || pparams->demoplayback || pparams->spectator )
	{
		pd.driftmove = 0;
		pd.pitchvel = 0;
		return;
	}

	// don't count small mouse motion
	if (pd.nodrift)
	{
		if ( fabs( pparams->cmd->forwardmove ) < kForwardSpeed )
			pd.driftmove = 0;
		else
			pd.driftmove += pparams->frametime;
	
		if ( pd.driftmove > v_centermove->value)
		{
			V_StartPitchDrift ();
		}
		return;
	}
	
	delta = pparams->idealpitch - pparams->cl_viewangles[PITCH];

	if (!delta)
	{
		pd.pitchvel = 0;
		return;
	}

	move = pparams->frametime * pd.pitchvel;
	pd.pitchvel += pparams->frametime * v_centerspeed->value;
	
//Con_Printf ("move: %f (%f)\n", move, pparams->frametime);

	if (delta > 0)
	{
		if (move > delta)
		{
			pd.pitchvel = 0;
			move = delta;
		}
		pparams->cl_viewangles[PITCH] += move;
	}
	else if (delta < 0)
	{
		if (move > -delta)
		{
			pd.pitchvel = 0;
			move = -delta;
		}
		pparams->cl_viewangles[PITCH] -= move;
	}
}

/* 
============================================================================== 
						VIEW RENDERING 
============================================================================== 
*/ 

/*
==================
V_CalcGunAngle
==================
*/
void V_CalcGunAngle ( struct ref_params_s *pparams )
{	
	cl_entity_t *viewent;
	
	viewent = gEngfuncs.GetViewModel();
	if ( !viewent )
		return;

	viewent->angles[YAW]   =  pparams->viewangles[YAW]   + pparams->crosshairangle[YAW];
	viewent->angles[PITCH] = -pparams->viewangles[PITCH] + pparams->crosshairangle[PITCH] * 0.25;
	viewent->angles[ROLL]  -= v_idlescale * sin(pparams->time*v_iroll_cycle.value) * v_iroll_level.value;
	
	// don't apply all of the v_ipitch to prevent normally unseen parts of viewmodel from coming into view.
	viewent->angles[PITCH] -= v_idlescale * sin(pparams->time*v_ipitch_cycle.value) * (v_ipitch_level.value * 0.5);
	viewent->angles[YAW]   -= v_idlescale * sin(pparams->time*v_iyaw_cycle.value) * v_iyaw_level.value;

	VectorCopy( viewent->angles, viewent->curstate.angles );
	VectorCopy( viewent->angles, viewent->latched.prevangles );
}

/*
==============
V_AddIdle

Idle swaying
==============
*/
void V_AddIdle ( struct ref_params_s *pparams )
{
	pparams->viewangles[ROLL] += v_idlescale * sin(pparams->time*v_iroll_cycle.value) * v_iroll_level.value;
	pparams->viewangles[PITCH] += v_idlescale * sin(pparams->time*v_ipitch_cycle.value) * v_ipitch_level.value;
	pparams->viewangles[YAW] += v_idlescale * sin(pparams->time*v_iyaw_cycle.value) * v_iyaw_level.value;
}


/*
==============
V_CalcViewRoll

Roll is induced by movement and damage
==============
*/
void V_CalcViewRoll ( struct ref_params_s *pparams )
{
	float		side;
	cl_entity_t *viewentity;
	
	viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity );
	if ( !viewentity )
		return;

	side = V_CalcRoll ( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed );

	pparams->viewangles[ROLL] += side;

	if ( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) )
	{
		// only roll the view if the player is dead and the viewheight[2] is nonzero 
		// this is so deadcam in multiplayer will work.
		pparams->viewangles[ROLL] = 80;	// dead view angle
		return;
	}
}


/*
==================
V_CalcIntermissionRefdef

==================
*/
void V_CalcIntermissionRefdef ( struct ref_params_s *pparams )
{
	cl_entity_t	*ent, *view;
	float		old;

	// ent is the player model ( visible when out of body )
	ent = gEngfuncs.GetLocalPlayer();
	
	// view is the weapon model (only visible from inside body )
	view = gEngfuncs.GetViewModel();

	VectorCopy ( pparams->simorg, pparams->vieworg );
	VectorCopy ( pparams->cl_viewangles, pparams->viewangles );

	view->model = NULL;

	// allways idle in intermission
	old = v_idlescale;
	v_idlescale = 1;

	V_AddIdle ( pparams );

	if ( gEngfuncs.IsSpectateOnly() )
	{
        // in HLTV we must go to 'intermission' position by ourself
        VectorCopy( gHUD.m_Spectator.m_cameraOrigin, pparams->vieworg );
		VectorCopy( gHUD.m_Spectator.m_cameraAngles, pparams->viewangles );
	}

	v_idlescale = old;

	v_cl_angles = pparams->cl_viewangles;
	v_origin = pparams->vieworg;
	v_angles = pparams->viewangles;
    v_view_ofs = pparams->viewheight;

}

#define ORIGIN_BACKUP 64
#define ORIGIN_MASK ( ORIGIN_BACKUP - 1 )

typedef struct 
{
	float Origins[ ORIGIN_BACKUP ][3];
	float OriginTime[ ORIGIN_BACKUP ];

	float Angles[ ORIGIN_BACKUP ][3];
	float AngleTime[ ORIGIN_BACKUP ];

	int CurrentOrigin;
	int CurrentAngle;
} viewinterp_t;

/*
==================
V_CalcRefdef

==================
*/
void V_CalcNormalRefdef ( struct ref_params_s *pparams )
{
	cl_entity_t		*ent, *view;
	int				i;
	vec3_t			angles;
	float			bob, waterOffset;
	static viewinterp_t		ViewInterp;

	static float oldz = 0;
	static float lasttime;

	vec3_t camAngles, camForward, camRight, camUp;
	cl_entity_t *pwater;

	V_DriftPitch ( pparams );

	if ( gEngfuncs.IsSpectateOnly() )
	{
		ent = gEngfuncs.GetEntityByIndex( g_iUser2 );
	}
	else
	{
		// ent is the player model ( visible when out of body )
		ent = gEngfuncs.GetLocalPlayer();
	}
	
	// view is the weapon model (only visible from inside body )
	view = gEngfuncs.GetViewModel();

	// transform the view offset by the model's matrix to get the offset from
	// model origin for the view
	bob = V_CalcBob ( pparams );

	// refresh position
	VectorCopy ( pparams->simorg, pparams->vieworg );
	pparams->vieworg[2] += ( bob );
	VectorAdd( pparams->vieworg, pparams->viewheight, pparams->vieworg );

	VectorCopy ( pparams->cl_viewangles, pparams->viewangles );

	gEngfuncs.V_CalcShake();
	gEngfuncs.V_ApplyShake( pparams->vieworg, pparams->viewangles, 1.0 );

	// never let view origin sit exactly on a node line, because a water plane can
	// dissapear when viewed with the eye exactly on it.
	// FIXME, we send origin at 1/128 now, change this?
	// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
	
	pparams->vieworg[0] += 1.0/32;
	pparams->vieworg[1] += 1.0/32;
	pparams->vieworg[2] += 1.0/32;

	// Check for problems around water, move the viewer artificially if necessary 
	// -- this prevents drawing errors in GL due to waves

	waterOffset = 0;
	if ( pparams->waterlevel >= 2 )
	{
		int		i, contents, waterDist, waterEntity;
		vec3_t	point;
		waterDist = cl_waterdist->value;

		if ( pparams->hardware )
		{
			waterEntity = gEngfuncs.PM_WaterEntity( pparams->simorg );
			if ( waterEntity >= 0 && waterEntity < pparams->max_entities )
			{
				pwater = gEngfuncs.GetEntityByIndex( waterEntity );
				if ( pwater && ( pwater->model != NULL ) )
				{
					waterDist += ( pwater->curstate.scale * 16 );	// Add in wave height
				}
			}
		}
		else
		{
			waterEntity = 0;	// Don't need this in software
		}
		
		VectorCopy( pparams->vieworg, point );

		// Eyes are above water, make sure we're above the waves
		if ( pparams->waterlevel == 2 )	
		{
			point[2] -= waterDist;
			for ( i = 0; i < waterDist; i++ )
			{
				contents = gEngfuncs.PM_PointContents( point, NULL );
				if ( contents > CONTENTS_WATER )
					break;
				point[2] += 1;
			}
			waterOffset = (point[2] + waterDist) - pparams->vieworg[2];
		}
		else
		{
			// eyes are under water.  Make sure we're far enough under
			point[2] += waterDist;

			for ( i = 0; i < waterDist; i++ )
			{
				contents = gEngfuncs.PM_PointContents( point, NULL );
				if ( contents <= CONTENTS_WATER )
					break;
				point[2] -= 1;
			}
			waterOffset = (point[2] - waterDist) - pparams->vieworg[2];
		}
	}

	pparams->vieworg[2] += waterOffset;
	
	V_CalcViewRoll ( pparams );
	
	V_AddIdle ( pparams );

	// offsets
	VectorCopy( pparams->cl_viewangles, angles );

	AngleVectors ( angles, pparams->forward, pparams->right, pparams->up );

	// don't allow cheats in multiplayer
	if ( pparams->maxclients <= 1 )
	{
		for ( i=0 ; i<3 ; i++ )
		{
			pparams->vieworg[i] += scr_ofsx->value*pparams->forward[i] + scr_ofsy->value*pparams->right[i] + scr_ofsz->value*pparams->up[i];
		}
	}
	
	// Treating cam_ofs[2] as the distance
	if( CL_IsThirdPerson() )
	{
		vec3_t ofs;

		ofs[0] = ofs[1] = ofs[2] = 0.0;

		CL_CameraOffset( (float *)&ofs );

		VectorCopy( ofs, camAngles );
		camAngles[ ROLL ]	= 0;

		AngleVectors( camAngles, camForward, camRight, camUp );

		for ( i = 0; i < 3; i++ )
		{
			pparams->vieworg[ i ] += -ofs[2] * camForward[ i ];
		}
	}
	
	// Give gun our viewangles
	VectorCopy ( pparams->cl_viewangles, view->angles );
	
	// set up gun position
	V_CalcGunAngle ( pparams );

	// Use predicted origin as view origin.
	VectorCopy ( pparams->simorg, view->origin );      
	view->origin[2] += ( waterOffset );
	VectorAdd( view->origin, pparams->viewheight, view->origin );

	// Let the viewmodel shake at about 10% of the amplitude
	gEngfuncs.V_ApplyShake( view->origin, view->angles, 0.9 );

	for ( i = 0; i < 3; i++ )
	{
		view->origin[ i ] += bob * 0.4 * pparams->forward[ i ];
	}
	view->origin[2] += bob;

	// throw in a little tilt.
	view->angles[YAW]   -= bob * 0.5;
	view->angles[ROLL]  -= bob * 1;
	view->angles[PITCH] -= bob * 0.3;

	// pushing the view origin down off of the same X/Z plane as the ent's origin will give the
	// gun a very nice 'shifting' effect when the player looks up/down. If there is a problem
	// with view model distortion, this may be a cause. (SJB). 
	view->origin[2] -= 1;

	// fudge position around to keep amount of weapon visible
	// roughly equal with different FOV
	if (pparams->viewsize == 110)
	{
		view->origin[2] += 1;
	}
	else if (pparams->viewsize == 100)
	{
		view->origin[2] += 2;
	}
	else if (pparams->viewsize == 90)
	{
		view->origin[2] += 1;
	}
	else if (pparams->viewsize == 80)
	{
		view->origin[2] += 0.5;
	}

	// Add in the punchangle, if any
	VectorAdd ( pparams->viewangles, pparams->punchangle, pparams->viewangles );

	// Include client side punch, too
	VectorAdd ( pparams->viewangles, (float *)&ev_punchangle, pparams->viewangles);

	V_DropPunchAngle ( pparams->frametime, (float *)&ev_punchangle );

	// smooth out stair step ups
#if 1
	if ( !pparams->smoothing && pparams->onground && pparams->simorg[2] - oldz > 0)
	{
		float steptime;
		
		steptime = pparams->time - lasttime;
		if (steptime < 0)
	//FIXME		I_Error ("steptime < 0");
			steptime = 0;

		oldz += steptime * 150;
		if (oldz > pparams->simorg[2])
			oldz = pparams->simorg[2];
		if (pparams->simorg[2] - oldz > 18)
			oldz = pparams->simorg[2]- 18;
		pparams->vieworg[2] += oldz - pparams->simorg[2];
		view->origin[2] += oldz - pparams->simorg[2];
	}
	else
	{
		oldz = pparams->simorg[2];
	}
#endif

	{
		static float lastorg[3];
		vec3_t delta;

		VectorSubtract( pparams->simorg, lastorg, delta );

		if ( Length( delta ) != 0.0 )
		{
			VectorCopy( pparams->simorg, ViewInterp.Origins[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] );
			ViewInterp.OriginTime[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] = pparams->time;
			ViewInterp.CurrentOrigin++;

			VectorCopy( pparams->simorg, lastorg );
		}
	}

	// Smooth out whole view in multiplayer when on trains, lifts
	if ( cl_vsmoothing && cl_vsmoothing->value &&
		( pparams->smoothing && ( pparams->maxclients > 1 ) ) )
	{
		int foundidx;
		int i;
		float t;

		if ( cl_vsmoothing->value < 0.0 )
		{
			gEngfuncs.Cvar_SetValue( "cl_vsmoothing", 0.0 );
		}

		t = pparams->time - cl_vsmoothing->value;

		for ( i = 1; i < ORIGIN_MASK; i++ )
		{
			foundidx = ViewInterp.CurrentOrigin - 1 - i;
			if ( ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] <= t )
				break;
		}

		if ( i < ORIGIN_MASK &&  ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] != 0.0 )
		{
			// Interpolate
			vec3_t delta;
			double frac;
			double dt;
			vec3_t neworg;

			dt = ViewInterp.OriginTime[ (foundidx + 1) & ORIGIN_MASK ] - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ];
			if ( dt > 0.0 )
			{
				frac = ( t - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK] ) / dt;
				frac = min( 1.0, frac );
				VectorSubtract( ViewInterp.Origins[ ( foundidx + 1 ) & ORIGIN_MASK ], ViewInterp.Origins[ foundidx & ORIGIN_MASK ], delta );
				VectorMA( ViewInterp.Origins[ foundidx & ORIGIN_MASK ], frac, delta, neworg );

				// Dont interpolate large changes
				if ( Length( delta ) < 64 )
				{
					VectorSubtract( neworg, pparams->simorg, delta );

					VectorAdd( pparams->simorg, delta, pparams->simorg );
					VectorAdd( pparams->vieworg, delta, pparams->vieworg );
					VectorAdd( view->origin, delta, view->origin );

				}
			}
		}
	}

	// Store off v_angles before munging for third person
	v_angles = pparams->viewangles;
	v_lastAngles = pparams->viewangles;
//	v_cl_angles = pparams->cl_viewangles;	// keep old user mouse angles !
	if ( CL_IsThirdPerson() )
	{
		VectorCopy( camAngles, pparams->viewangles);
		float pitch = camAngles[ 0 ];

		// Normalize angles
		if ( pitch > 180 ) 
			pitch -= 360.0;
		else if ( pitch < -180 )
			pitch += 360;

		// Player pitch is inverted
		pitch /= -3.0;

		// Slam local player's pitch value
		ent->angles[ 0 ] = pitch;
		ent->curstate.angles[ 0 ] = pitch;
		ent->prevstate.angles[ 0 ] = pitch;
		ent->latched.prevangles[ 0 ] = pitch;
	}

	// override all previous settings if the viewent isn't the client
	if ( pparams->viewentity > pparams->maxclients )
	{
		cl_entity_t *viewentity;
		viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity );
		if ( viewentity )
		{
			VectorCopy( viewentity->origin, pparams->vieworg );
			VectorCopy( viewentity->angles, pparams->viewangles );

			// Store off overridden viewangles
			v_angles = pparams->viewangles;
		}
	}

	lasttime = pparams->time;

	v_origin = pparams->vieworg;
    v_view_ofs = pparams->viewheight;

}

/*
==================
V_CalcTopDownRefdef

==================
*/
void V_CalcTopDownRefdef ( struct ref_params_s *pparams )
{
	cl_entity_t		*ent, *view;
	int				i;
	vec3_t			angles;
	//float			waterOffset;
	static viewinterp_t		ViewInterp;

	static float oldz = 0;
	static float lasttime;

	vec3_t camAngles, camForward, camRight, camUp;
	//cl_entity_t *pwater;

	V_DriftPitch ( pparams );

	if ( gEngfuncs.IsSpectateOnly() )
	{
		ent = gEngfuncs.GetEntityByIndex( g_iUser2 );
	}
	else
	{
		// ent is the player model ( visible when out of body )
		ent = gEngfuncs.GetLocalPlayer();
	}
	
	// view is the weapon model (only visible from inside body )
	view = gEngfuncs.GetViewModel();

	// Override topdown position and angles from physics code
	VectorCopy( gTopDownViewOrigin, pparams->vieworg );
	VectorCopy( gTopDownViewOrigin, pparams->simorg );

	VectorCopy( gTopDownViewAngles, pparams->cl_viewangles );
	VectorCopy( gTopDownViewAngles, pparams->viewangles );

	gEngfuncs.V_CalcShake();
	gEngfuncs.V_ApplyShake( pparams->vieworg, pparams->viewangles, 1.0 );

//	// never let view origin sit exactly on a node line, because a water plane can
//	// dissapear when viewed with the eye exactly on it.
//	// FIXME, we send origin at 1/128 now, change this?
//	// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
//	
//	pparams->vieworg[0] += 1.0/32;
//	pparams->vieworg[1] += 1.0/32;
//	pparams->vieworg[2] += 1.0/32;
//
//	// Check for problems around water, move the viewer artificially if necessary 
//	// -- this prevents drawing errors in GL due to waves
//
//	waterOffset = 0;
//	if ( pparams->waterlevel >= 2 )
//	{
//		int		i, contents, waterDist, waterEntity;
//		vec3_t	point;
//		waterDist = cl_waterdist->value;
//
//		if ( pparams->hardware )
//		{
//			waterEntity = gEngfuncs.PM_WaterEntity( pparams->simorg );
//			if ( waterEntity >= 0 && waterEntity < pparams->max_entities )
//			{
//				pwater = gEngfuncs.GetEntityByIndex( waterEntity );
//				if ( pwater && ( pwater->model != NULL ) )
//				{
//					waterDist += ( pwater->curstate.scale * 16 );	// Add in wave height
//				}
//			}
//		}
//		else
//		{
//			waterEntity = 0;	// Don't need this in software
//		}
//		
//		VectorCopy( pparams->vieworg, point );
//
//		// Eyes are above water, make sure we're above the waves
//		if ( pparams->waterlevel == 2 )	
//		{
//			point[2] -= waterDist;
//			for ( i = 0; i < waterDist; i++ )
//			{
//				contents = gEngfuncs.PM_PointContents( point, NULL );
//				if ( contents > CONTENTS_WATER )
//					break;
//				point[2] += 1;
//			}
//			waterOffset = (point[2] + waterDist) - pparams->vieworg[2];
//		}
//		else
//		{
//			// eyes are under water.  Make sure we're far enough under
//			point[2] += waterDist;
//
//			for ( i = 0; i < waterDist; i++ )
//			{
//				contents = gEngfuncs.PM_PointContents( point, NULL );
//				if ( contents <= CONTENTS_WATER )
//					break;
//				point[2] -= 1;
//			}
//			waterOffset = (point[2] - waterDist) - pparams->vieworg[2];
//		}
//	}
//
//	pparams->vieworg[2] += waterOffset;
	
	V_CalcViewRoll ( pparams );
	
	//V_AddIdle ( pparams );

	// offsets
	VectorCopy( pparams->cl_viewangles, angles );

	AngleVectors ( angles, pparams->forward, pparams->right, pparams->up );

	// don't allow cheats in multiplayer
	if ( pparams->maxclients <= 1 )
	{
		for ( i=0 ; i<3 ; i++ )
		{
			pparams->vieworg[i] += scr_ofsx->value*pparams->forward[i] + scr_ofsy->value*pparams->right[i] + scr_ofsz->value*pparams->up[i];
		}
	}
	
	// Treating cam_ofs[2] as the distance
//	if( CL_IsThirdPerson() )
//	{
//		vec3_t ofs;
//
//		ofs[0] = ofs[1] = ofs[2] = 0.0;
//
//		CL_CameraOffset( (float *)&ofs );
//
//		VectorCopy( ofs, camAngles );
//		camAngles[ ROLL ]	= 0;
//
//		AngleVectors( camAngles, camForward, camRight, camUp );
//
//		for ( i = 0; i < 3; i++ )
//		{
//			pparams->vieworg[ i ] += -ofs[2] * camForward[ i ];
//		}
//	}
	
	// Give gun our viewangles
//	VectorCopy ( pparams->cl_viewangles, view->angles );
	
	// set up gun position
//	V_CalcGunAngle ( pparams );

	// Use predicted origin as view origin.
//	VectorCopy ( pparams->simorg, view->origin );      
////	view->origin[2] += ( waterOffset );
//	VectorAdd( view->origin, pparams->viewheight, view->origin );

	// Let the viewmodel shake at about 10% of the amplitude
//	gEngfuncs.V_ApplyShake( view->origin, view->angles, 0.9 );

//	for ( i = 0; i < 3; i++ )
//	{
//		view->origin[ i ] += bob * 0.4 * pparams->forward[ i ];
//	}
//	view->origin[2] += bob;
//
//	// throw in a little tilt.
//	view->angles[YAW]   -= bob * 0.5;
//	view->angles[ROLL]  -= bob * 1;
//	view->angles[PITCH] -= bob * 0.3;
//
//	// pushing the view origin down off of the same X/Z plane as the ent's origin will give the
//	// gun a very nice 'shifting' effect when the player looks up/down. If there is a problem
//	// with view model distortion, this may be a cause. (SJB). 
//	view->origin[2] -= 1;
//
//	// fudge position around to keep amount of weapon visible
//	// roughly equal with different FOV
//	if (pparams->viewsize == 110)
//	{
//		view->origin[2] += 1;
//	}
//	else if (pparams->viewsize == 100)
//	{
//		view->origin[2] += 2;
//	}
//	else if (pparams->viewsize == 90)
//	{
//		view->origin[2] += 1;
//	}
//	else if (pparams->viewsize == 80)
//	{
//		view->origin[2] += 0.5;
//	}

	// Add in the punchangle, if any
//	VectorAdd ( pparams->viewangles, pparams->punchangle, pparams->viewangles );
//
//	// Include client side punch, too
////	VectorAdd ( pparams->viewangles, (float *)&ev_punchangle, pparams->viewangles);
//
//	V_DropPunchAngle ( pparams->frametime, (float *)&ev_punchangle );

	// smooth out stair step ups
#if 1
	if ( !pparams->smoothing && pparams->onground && pparams->simorg[2] - oldz > 0)
	{
		float steptime;
		
		steptime = pparams->time - lasttime;
		if (steptime < 0)
	//FIXME		I_Error ("steptime < 0");
			steptime = 0;

		oldz += steptime * 150;
		if (oldz > pparams->simorg[2])
			oldz = pparams->simorg[2];
		if (pparams->simorg[2] - oldz > 18)
			oldz = pparams->simorg[2]- 18;
		pparams->vieworg[2] += oldz - pparams->simorg[2];
		view->origin[2] += oldz - pparams->simorg[2];
	}
	else
	{
		oldz = pparams->simorg[2];
	}
#endif

	{
		static float lastorg[3];
		vec3_t delta;

		VectorSubtract( pparams->simorg, lastorg, delta );

		if ( Length( delta ) != 0.0 )
		{
			VectorCopy( pparams->simorg, ViewInterp.Origins[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] );
			ViewInterp.OriginTime[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] = pparams->time;
			ViewInterp.CurrentOrigin++;

			VectorCopy( pparams->simorg, lastorg );
		}
	}

	// Smooth out whole view in multiplayer when on trains, lifts
	if ( cl_vsmoothing && cl_vsmoothing->value &&
		( pparams->smoothing && ( pparams->maxclients > 1 ) ) )
	{
		int foundidx;
		int i;
		float t;

		if ( cl_vsmoothing->value < 0.0 )
		{
			gEngfuncs.Cvar_SetValue( "cl_vsmoothing", 0.0 );
		}

		t = pparams->time - cl_vsmoothing->value;

		for ( i = 1; i < ORIGIN_MASK; i++ )
		{
			foundidx = ViewInterp.CurrentOrigin - 1 - i;
			if ( ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] <= t )
				break;
		}

		if ( i < ORIGIN_MASK &&  ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] != 0.0 )
		{
			// Interpolate
			vec3_t delta;
			double frac;
			double dt;
			vec3_t neworg;

			dt = ViewInterp.OriginTime[ (foundidx + 1) & ORIGIN_MASK ] - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ];
			if ( dt > 0.0 )
			{
				frac = ( t - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK] ) / dt;
				frac = min( 1.0, frac );
				VectorSubtract( ViewInterp.Origins[ ( foundidx + 1 ) & ORIGIN_MASK ], ViewInterp.Origins[ foundidx & ORIGIN_MASK ], delta );
				VectorMA( ViewInterp.Origins[ foundidx & ORIGIN_MASK ], frac, delta, neworg );

				// Dont interpolate large changes
				if ( Length( delta ) < 64 )
				{
					VectorSubtract( neworg, pparams->simorg, delta );

					VectorAdd( pparams->simorg, delta, pparams->simorg );
					VectorAdd( pparams->vieworg, delta, pparams->vieworg );
					VectorAdd( view->origin, delta, view->origin );

					//VectorCopy( pparams->simorg, gTopDownViewOrigin );
				}
			}
		}
	}

	// Store off v_angles before munging for third person
	v_angles = pparams->viewangles;
	v_lastAngles = pparams->viewangles;
//	v_cl_angles = pparams->cl_viewangles;	// keep old user mouse angles !
//	if ( CL_IsThirdPerson() )
//	{
//		VectorCopy( camAngles, pparams->viewangles);
//		float pitch = camAngles[ 0 ];
//
//		// Normalize angles
//		if ( pitch > 180 ) 
//			pitch -= 360.0;
//		else if ( pitch < -180 )
//			pitch += 360;
//
//		// Player pitch is inverted
//		pitch /= -3.0;
//
//		// Slam local player's pitch value
//		ent->angles[ 0 ] = pitch;
//		ent->curstate.angles[ 0 ] = pitch;
//		ent->prevstate.angles[ 0 ] = pitch;
//		ent->latched.prevangles[ 0 ] = pitch;
//	}

//	// override all previous settings if the viewent isn't the client
//	if ( pparams->viewentity > pparams->maxclients )
//	{
//		cl_entity_t *viewentity;
//		viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity );
//		if ( viewentity )
//		{
//			VectorCopy( viewentity->origin, pparams->vieworg );
//			VectorCopy( viewentity->angles, pparams->viewangles );
//
//			// Store off overridden viewangles
//			v_angles = pparams->viewangles;
//		}
//	}

	lasttime = pparams->time;

	v_origin = pparams->vieworg;
    v_view_ofs = pparams->viewheight;

}

void V_SmoothInterpolateAngles( float * startAngle, float * endAngle, float * finalAngle, float degreesPerSec )
{
	float absd,frac,d,threshhold;
	
	NormalizeAngles( startAngle );
	NormalizeAngles( endAngle );

	for ( int i = 0 ; i < 3 ; i++ )
	{
		d = endAngle[i] - startAngle[i];

		if ( d > 180.0f )
		{
			d -= 360.0f;
		}
		else if ( d < -180.0f )
		{	
			d += 360.0f;
		}

		absd = fabs(d);

		if ( absd > 0.01f )
		{
			frac = degreesPerSec * v_frametime;

			threshhold= degreesPerSec / 4;

			if ( absd < threshhold )
			{
				float h = absd / threshhold;
				h *= h;
				frac*= h;  // slow down last degrees
			}

			if ( frac >  absd )
			{
				finalAngle[i] = endAngle[i];
			}
			else
			{
				if ( d>0)
					finalAngle[i] = startAngle[i] + frac;
				else
					finalAngle[i] = startAngle[i] - frac;
			}
		}
		else
		{
			finalAngle[i] = endAngle[i];
		}

	}

	NormalizeAngles( finalAngle );
}

// Get the origin of the Observer based around the target's position and angles
void V_GetChaseOrigin( float * angles, float * origin, float distance, float * returnvec )
{
	vec3_t	vecEnd;
	vec3_t	forward;
	vec3_t	vecStart;
	pmtrace_t * trace;
	int maxLoops = 8;

	int ignoreent = -1;	// first, ignore no entity
	
	cl_entity_t	 *	ent = NULL;
	
	// Trace back from the target using the player's view angles
	AngleVectors(angles, forward, NULL, NULL);
	
	VectorScale(forward,-1,forward);

	VectorCopy( origin, vecStart );

	VectorMA(vecStart, distance , forward, vecEnd);

	while ( maxLoops > 0)
	{
		trace = gEngfuncs.PM_TraceLine( vecStart, vecEnd, PM_TRACELINE_PHYSENTSONLY, 2, ignoreent );

		// WARNING! trace->ent is is the number in physent list not the normal entity number

		if ( trace->ent <= 0)
			break;	// we hit the world or nothing, stop trace

		ent = gEngfuncs.GetEntityByIndex( PM_GetPhysEntInfo( trace->ent ) );

		if ( ent == NULL )
			break;

		// hit non-player solid BSP , stop here
		if ( ent->curstate.solid == SOLID_BSP && !ent->player ) 
			break;

		// if close enought to end pos, stop, otherwise continue trace
		if( VectorDistance(trace->endpos, vecEnd ) < 1.0f )
		{
			break;
		}
		else
		{
			ignoreent = trace->ent;	// ignore last hit entity
			VectorCopy( trace->endpos, vecStart);
		}

		maxLoops--;
	}  

/*	if ( ent )
	{
		gEngfuncs.Con_Printf("Trace loops %i , entity %i, model %s, solid %i\n",(8-maxLoops),ent->curstate.number, ent->model->name , ent->curstate.solid ); 
	} */

	VectorMA( trace->endpos, 4, trace->plane.normal, returnvec );

	v_lastDistance = VectorDistance(trace->endpos, origin);	// real distance without offset
}

/*void V_GetDeathCam(cl_entity_t * ent1, cl_entity_t * ent2, float * angle, float * origin)
{
	float newAngle[3]; float newOrigin[3]; 

	float distance = 168.0f;

	v_lastDistance+= v_frametime * 96.0f;	// move unit per seconds back

	if ( v_resetCamera )
		v_lastDistance = 64.0f;

	if ( distance > v_lastDistance )
		distance = v_lastDistance;

	VectorCopy(ent1->origin, newOrigin);

	if ( ent1->player )
		newOrigin[2]+= 17; // head level of living player

	// get new angle towards second target
	if ( ent2 )
	{
		VectorSubtract( ent2->origin, ent1->origin, newAngle );
		VectorAngles( newAngle, newAngle );
		newAngle[0] = -newAngle[0];
	}
	else
	{
		// if no second target is given, look down to dead player
		newAngle[0] = 90.0f;
		newAngle[1] = 0.0f;
		newAngle[2] = 0;
	}

	// and smooth view
	V_SmoothInterpolateAngles( v_lastAngles, newAngle, angle, 120.0f );
			
	V_GetChaseOrigin( angle, newOrigin, distance, origin );

	VectorCopy(angle, v_lastAngles);
}*/

void V_GetSingleTargetCam(cl_entity_t * ent1, float * angle, float * origin)
{
	float newAngle[3]; float newOrigin[3]; 
	
	int flags 	   = gHUD.m_Spectator.m_iObserverFlags;

	// see is target is a dead player
	qboolean deadPlayer = ent1->player && (ent1->curstate.solid == SOLID_NOT);
	
	float dfactor   = ( flags & DRC_FLAG_DRAMATIC )? -1.0f : 1.0f;

	float distance = 112.0f + ( 16.0f * dfactor ); // get close if dramatic;
	
	// go away in final scenes or if player just died
	if ( flags & DRC_FLAG_FINAL )
		distance*=2.0f;	
	else if ( deadPlayer )
		distance*=1.5f;	

	// let v_lastDistance float smoothly away
	v_lastDistance+= v_frametime * 32.0f;	// move unit per seconds back

	if ( distance > v_lastDistance )
		distance = v_lastDistance;
	
	VectorCopy(ent1->origin, newOrigin);

	if ( ent1->player )
	{
		if ( deadPlayer )  
			newOrigin[2]+= 2;	//laying on ground
		else
			newOrigin[2]+= 17; // head level of living player
			
	}
	else
		newOrigin[2]+= 8;	// object, tricky, must be above bomb in CS

	// we have no second target, choose view direction based on
	// show front of primary target
	VectorCopy(ent1->angles, newAngle);

	// show dead players from front, normal players back
	if ( flags & DRC_FLAG_FACEPLAYER )
		newAngle[1]+= 180.0f;


	newAngle[0]+= 12.5f * dfactor; // lower angle if dramatic

	// if final scene (bomb), show from real high pos
	if ( flags & DRC_FLAG_FINAL )
		newAngle[0] = 22.5f; 

	// choose side of object/player			
	if ( flags & DRC_FLAG_SIDE )
		newAngle[1]+=22.5f;
	else
		newAngle[1]-=22.5f;

	V_SmoothInterpolateAngles( v_lastAngles, newAngle, angle, 120.0f );

	// HACK, if player is dead don't clip against his dead body, can't check this
	V_GetChaseOrigin( angle, newOrigin, distance, origin );
}

float MaxAngleBetweenAngles(  float * a1, float * a2 )
{
	float d, maxd = 0.0f;

	NormalizeAngles( a1 );
	NormalizeAngles( a2 );

	for ( int i = 0 ; i < 3 ; i++ )
	{
		d = a2[i] - a1[i];
		if ( d > 180 )
		{
			d -= 360;
		}
		else if ( d < -180 )
		{	
			d += 360;
		}

		d = fabs(d);

		if ( d > maxd )
			maxd=d;
	}

	return maxd;
}

void V_GetDoubleTargetsCam(cl_entity_t	 * ent1, cl_entity_t * ent2,float * angle, float * origin)
{
	float newAngle[3]; float newOrigin[3]; float tempVec[3];

	int flags 	   = gHUD.m_Spectator.m_iObserverFlags;

	float dfactor   = ( flags & DRC_FLAG_DRAMATIC )? -1.0f : 1.0f;

	float distance = 112.0f + ( 16.0f * dfactor ); // get close if dramatic;
	
	// go away in final scenes or if player just died
	if ( flags & DRC_FLAG_FINAL )
		distance*=2.0f;	
	
	// let v_lastDistance float smoothly away
	v_lastDistance+= v_frametime * 32.0f;	// move unit per seconds back

	if ( distance > v_lastDistance )
		distance = v_lastDistance;

	VectorCopy(ent1->origin, newOrigin);

	if ( ent1->player )
		newOrigin[2]+= 17; // head level of living player
	else
		newOrigin[2]+= 8;	// object, tricky, must be above bomb in CS

	// get new angle towards second target
	VectorSubtract( ent2->origin, ent1->origin, newAngle );

	VectorAngles( newAngle, newAngle );
	newAngle[0] = -newAngle[0];

	// set angle diffrent in Dramtaic scenes
	newAngle[0]+= 12.5f * dfactor; // lower angle if dramatic
			
	if ( flags & DRC_FLAG_SIDE )
		newAngle[1]+=22.5f;
	else
		newAngle[1]-=22.5f;

	float d = MaxAngleBetweenAngles( v_lastAngles, newAngle );
	
	if ( ( d < v_cameraFocusAngle) && ( v_cameraMode == CAM_MODE_RELAX ) )
	{
		// difference is to small and we are in relax camera mode, keep viewangles
		VectorCopy(v_lastAngles, newAngle );
	}
	else if ( (d < v_cameraRelaxAngle) && (v_cameraMode == CAM_MODE_FOCUS) )
	{
		// we catched up with our target, relax again
		v_cameraMode = CAM_MODE_RELAX;
	}
	else
	{
		// target move too far away, focus camera again
		v_cameraMode = CAM_MODE_FOCUS;
	}
	
	// and smooth view, if not a scene cut
	if ( v_resetCamera || (v_cameraMode == CAM_MODE_RELAX) )
	{
		VectorCopy( newAngle, angle );
	}
	else
	{
		V_SmoothInterpolateAngles( v_lastAngles, newAngle, angle, 180.0f );
	}
	
	V_GetChaseOrigin( newAngle, newOrigin, distance, origin );
	
	// move position up, if very close at target
	if ( v_lastDistance < 64.0f )
		origin[2]+= 16.0f*( 1.0f - (v_lastDistance / 64.0f ) );
	
	// calculate angle to second target
	VectorSubtract( ent2->origin, origin, tempVec );
	VectorAngles( tempVec, tempVec );
	tempVec[0] = -tempVec[0];
	
	/* take middle between two viewangles
	InterpolateAngles( newAngle, tempVec, newAngle, 0.5f); */
}


void V_GetDirectedChasePosition(cl_entity_t	 * ent1, cl_entity_t * ent2,float * angle, float * origin)
{

	if ( v_resetCamera )
	{
		v_lastDistance = 4096.0f;
		// v_cameraMode = CAM_MODE_FOCUS;
	}
	
	if ( ( ent2 == (cl_entity_t*)0xFFFFFFFF ) || ( ent1->player && (ent1->curstate.solid == SOLID_NOT) ) )
	{
		// we have no second target or player just died
		V_GetSingleTargetCam(ent1, angle, origin);
	}
	else if ( ent2 )
	{
		// keep both target in view
		V_GetDoubleTargetsCam( ent1, ent2, angle, origin );
	}
	else
	{
		// second target disappeard somehow (dead)
		
		// keep last good viewangle
		float newOrigin[3];
		
		int flags 	   = gHUD.m_Spectator.m_iObserverFlags;
		
		float dfactor   = ( flags & DRC_FLAG_DRAMATIC )? -1.0f : 1.0f;
		
		float distance = 112.0f + ( 16.0f * dfactor ); // get close if dramatic;
		
		// go away in final scenes or if player just died
		if ( flags & DRC_FLAG_FINAL )
			distance*=2.0f;	
		
		// let v_lastDistance float smoothly away
		v_lastDistance+= v_frametime * 32.0f;	// move unit per seconds back
		
		if ( distance > v_lastDistance )
			distance = v_lastDistance;
		
		VectorCopy(ent1->origin, newOrigin);
		
		if ( ent1->player )
			newOrigin[2]+= 17; // head level of living player
		else
			newOrigin[2]+= 8;	// object, tricky, must be above bomb in CS
		
		V_GetChaseOrigin( angle, newOrigin, distance, origin );
	}
	
	VectorCopy(angle, v_lastAngles);
}

void V_GetChasePos(int target, float * cl_angles, float * origin, float * angles)
{
	cl_entity_t	 *	ent = NULL;
	
	if ( target ) 
	{
		ent = gEngfuncs.GetEntityByIndex( target );
	};
	
	if (!ent)
	{
		// just copy a save in-map position
		VectorCopy ( vJumpAngles, angles );
		VectorCopy ( vJumpOrigin, origin );
		return;
	}
	
	
	
	if ( gHUD.m_Spectator.m_autoDirector->value )
	{
		if ( g_iUser3 )
			V_GetDirectedChasePosition( ent, gEngfuncs.GetEntityByIndex( g_iUser3 ),
			angles, origin );
		else
			V_GetDirectedChasePosition( ent, ( cl_entity_t*)0xFFFFFFFF,
			angles, origin );
	}
	else
	{
		if ( cl_angles == NULL )	// no mouse angles given, use entity angles ( locked mode )
		{
			VectorCopy ( ent->angles, angles);
			angles[0]*=-1;
		}
		else
			VectorCopy ( cl_angles, angles);
		
		
		VectorCopy ( ent->origin, origin);
		
		origin[2]+= 28; // DEFAULT_VIEWHEIGHT - some offset
		
		V_GetChaseOrigin( angles, origin, cl_chasedist->value, origin );
	}
	
	v_resetCamera = false;	
}

void V_ResetChaseCam()
{
	v_resetCamera = true;
}

void V_GetInEyePos(int entity, float *origin, float * angles )
{
	cl_entity_t	 * ent = gEngfuncs.GetEntityByIndex( entity );

	if ( !ent )
		return;

	if ( !ent->player || g_PlayerInfoList[entity].name == NULL )
		return;

	VectorCopy ( ent->origin, origin );
	VectorCopy ( ent->angles, angles );

	angles[0]*=-M_PI;

	if ( ent->curstate.solid == SOLID_NOT )
	{
		angles[ROLL] = 80;	// dead view angle
		origin[2]+= -8 ; // PM_DEAD_VIEWHEIGHT
	}
	else if (ent->curstate.usehull == 1 )
		origin[2]+= 12; // VEC_DUCK_VIEW;
	else
		// exacty eye position can't be caluculated since it depends on
		// client values like cl_bobcycle, this offset matches the default values
		origin[2]+= 28; // DEFAULT_VIEWHEIGHT
}

void V_GetMapFreePosition( float * cl_angles, float * origin, float * angles )
{
	vec3_t forward;
	vec3_t zScaledTarget;

	VectorCopy(cl_angles, angles);

	// modify angles since we don't wanna see map's bottom
	angles[0] = 51.25f + 38.75f*(angles[0]/90.0f);

	zScaledTarget[0] = gHUD.m_Spectator.m_mapOrigin[0];
	zScaledTarget[1] = gHUD.m_Spectator.m_mapOrigin[1];
	zScaledTarget[2] = gHUD.m_Spectator.m_mapOrigin[2] * (( 90.0f - angles[0] ) / 90.0f );
	

	AngleVectors(angles, forward, NULL, NULL);
	
	VectorNormalize(forward);
	
	VectorMA(zScaledTarget, -( 4096.0f / gHUD.m_Spectator.m_mapZoom ), forward , origin);
}

void V_GetMapChasePosition(int target, float * cl_angles, float * origin, float * angles)
{
	vec3_t forward;
	
	if ( target )
	{
		cl_entity_t	 *	ent = gEngfuncs.GetEntityByIndex( target );
		
		if ( gHUD.m_Spectator.m_autoDirector->value )
		{
			// this is done to get the angles made by director mode
			V_GetChasePos(target, cl_angles, origin, angles);
			VectorCopy(ent->origin, origin);
			
			// keep fix chase angle horizontal
			angles[0] = 45.0f;
		}
		else
		{
			VectorCopy(cl_angles, angles);
			VectorCopy(ent->origin, origin);
			
			// modify angles since we don't wanna see map's bottom
			angles[0] = 51.25f + 38.75f*(angles[0]/90.0f);
		}
	}
	else
	{
		// keep out roaming position, but modify angles
		VectorCopy(cl_angles, angles);
		angles[0] = 51.25f + 38.75f*(angles[0]/90.0f);
	}
	
	origin[2] *= (( 90.0f - angles[0] ) / 90.0f );
	angles[2] = 0.0f;	// don't roll angle (if chased player is dead)
	
	AngleVectors(angles, forward, NULL, NULL);
	
	VectorNormalize(forward);
	
	VectorMA(origin, -1536, forward, origin); 
}

void V_GetMiniMapOriginAndAngle(float * cl_angles, float * origin, float * angles)
{
	// Center map on local player
	VectorCopy(gEngfuncs.GetLocalPlayer()->curstate.origin, origin);
	//v_origin.z += 300;
	
	//// Set view height above parsed map settings
	origin[2] = gHUD.m_Spectator.m_OverviewData.origin.z + 100;
	
	//if(gHUD.m_Spectator.m_OverviewData.layers > 0)
	//{
	//	v_origin.z = gHUD.m_Spectator.m_OverviewData.layersHeights[0];
	//}
	//
	//v_origin.z += 300;
	
	//V_GetMapChasePosition(gEngfuncs.GetLocalPlayer()->index, v_cl_angles, v_origin, v_angles );
	
	// Set view like top down
	cl_angles[0] = angles[0] = 90;//kTopDownYaw;
	cl_angles[1] = angles[1] = 90;//kTopDownPitch;
	cl_angles[2] = angles[2] = 0;//kTopDownRoll;
}

int V_FindViewModelByWeaponModel(int weaponindex, int inUser3, int inUser4)
{
	int theViewModelIndex = 0;

	static char * modelmap[][2] =	{
		{ kKNPModel,			kKNVModel },
		{ kHGPModel,			kHGVModel },
		{ kMGPModel,			kMGVModel },
		{ kSGPModel,			kSGVModel },
		{ kHMGPModel,			kHMGVModel },
		{ kGGPModel,			kGGVModel },
		{ kTripminePModel,		kTripmineVModel },
		{ kWelderPModel,		kWelderVModel },
		{ NULL, NULL } 
	};

	static char * hvymodelmap[][2] =	{
		{ kKNPModel,			kKNHVVModel },
		{ kHGPModel,			kHGHVVModel },
		{ kMGPModel,			kMGHVVModel },
		{ kSGPModel,			kSGHVVModel },
		{ kHMGPModel,			kHMGHVVModel },
		{ kGGPModel,			kGGHVVModel },
		{ kTripminePModel,		kTripmineHVVModel },
		{ kWelderPModel,		kWelderHVVModel },
		{ NULL, NULL } 
	};

	struct model_s * weaponModel = NULL;

	// If we're an alien, get view model that way
	if((inUser3 == AVH_USER3_ALIEN_PLAYER1) || (inUser3 == AVH_USER3_ALIEN_PLAYER2) || (inUser3 == AVH_USER3_ALIEN_PLAYER3) || (inUser3 == AVH_USER3_ALIEN_PLAYER4) || (inUser3 == AVH_USER3_ALIEN_PLAYER5))
	{
		const char* theViewModel = "";
		switch(inUser3)
		{
		case AVH_USER3_ALIEN_PLAYER1:
			theViewModel = kLevel1ViewModel;
			break;
		case AVH_USER3_ALIEN_PLAYER2:
			theViewModel = kLevel2ViewModel;
			break;
		case AVH_USER3_ALIEN_PLAYER3:
			theViewModel = kLevel3ViewModel;
			break;
		case AVH_USER3_ALIEN_PLAYER4:
			theViewModel = kLevel4ViewModel;
			break;
		case AVH_USER3_ALIEN_PLAYER5:
			theViewModel = kLevel5ViewModel;
			break;
		}

		theViewModelIndex = gEngfuncs.pEventAPI->EV_FindModelIndex(theViewModel); 
	}
	// else we're holding a weapon
	else
	{
		// Take user3 and user4 into account for heavy armor
		if(inUser3 == AVH_USER3_MARINE_PLAYER)
		{
			weaponModel = IEngineStudio.GetModelByIndex( weaponindex );
			
			if ( weaponModel )
			{
				//int len = strlen( weaponModel->name );
				int i = 0;

				if(GetHasUpgrade(inUser4, MASK_UPGRADE_13))
				{
					while ( *hvymodelmap[i] != NULL )
					{
						const char* theCurrentPWeapon = hvymodelmap[i][0];
						const char* theCurrentVWeapon = hvymodelmap[i][1];

						if ( !SafeStrcmp( weaponModel->name, theCurrentPWeapon) )
						{
							theViewModelIndex = gEngfuncs.pEventAPI->EV_FindModelIndex( theCurrentVWeapon );
						}
						i++;
					}
				}
				else
				{
					while ( *modelmap[i] != NULL )
					{
						const char* theCurrentPWeapon = modelmap[i][0];
						const char* theCurrentVWeapon = modelmap[i][1];

						if ( !SafeStrcmp( weaponModel->name, theCurrentPWeapon) )
						{
							theViewModelIndex = gEngfuncs.pEventAPI->EV_FindModelIndex( theCurrentVWeapon );
						}
						i++;
					}
				}
			}
		}
	}

	return theViewModelIndex;
}

/*
==================
V_CalcSpectatorRefdef

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

static int lastWeaponModelIndex = 0;
static int lastViewModelIndex = 0;

void V_CalcSpectatorRefdef ( struct ref_params_s * pparams )
{

	vec3_t					angles;
	static viewinterp_t		ViewInterp;
	static float			bob = 0.0f;
	static vec3_t			velocity ( 0.0f, 0.0f, 0.0f);

	static float lasttime;

	static float lastang[3];
	static float lastorg[3];

	vec3_t delta;
	pparams->onlyClientDraw = false;

	// refresh position
	VectorCopy ( pparams->simorg, v_sim_org );

	// get old values
	VectorCopy ( pparams->cl_viewangles, v_cl_angles );
	VectorCopy ( pparams->viewangles, v_angles );
	VectorCopy ( pparams->vieworg, v_origin );
    VectorCopy ( pparams->viewheight, v_view_ofs);

	v_frametime = pparams->frametime;

	if ( pparams->nextView == 0 )
	{
		// first renderer cycle, full screen

        if (!gHUD.m_Spectator.IsInOverviewMode())
        {
            
            switch ( g_iUser1 )
		    {
			    case OBS_CHASE_LOCKED:	V_GetChasePos( g_iUser2, NULL, v_origin, v_angles );
									    break;

			    case OBS_CHASE_FREE:	V_GetChasePos( g_iUser2, v_cl_angles, v_origin, v_angles );
									    break;

			    case OBS_ROAMING	:	VectorCopy (v_cl_angles, v_angles);
									    VectorCopy (v_sim_org, v_origin);
									    break;

			    case OBS_IN_EYE		:   V_GetInEyePos( g_iUser2, v_origin, v_angles );
									    break;
				    
		    }          
            
        }
        else
        {
            pparams->onlyClientDraw = true;
            V_GetMapFreePosition( v_cl_angles, v_origin, v_angles );        
        }
        

        // Removed by mmcguire.
        /*
		switch ( g_iUser1 )
		{
			case OBS_CHASE_LOCKED:	V_GetChasePos( g_iUser2, NULL, v_origin, v_angles );
									break;

			case OBS_CHASE_FREE:	V_GetChasePos( g_iUser2, v_cl_angles, v_origin, v_angles );
									break;

			case OBS_ROAMING	:	VectorCopy (v_cl_angles, v_angles);
									VectorCopy (v_sim_org, v_origin);
									break;

			case OBS_IN_EYE		:   V_GetInEyePos( g_iUser2, v_origin, v_angles );
									break;
				
			case OBS_MAP_FREE  :	pparams->onlyClientDraw = true;
									V_GetMapFreePosition( v_cl_angles, v_origin, v_angles );
									break;

			case OBS_MAP_CHASE  :	pparams->onlyClientDraw = true;
									V_GetMapChasePosition( g_iUser2, v_cl_angles, v_origin, v_angles );
									break;
		}
        */

        if (g_iUser1)
        {
		
            // Adjust the viewport so that the letterbox spectator mode
            // doesn't cut anything off.
            
            pparams->viewport[1] += YRES(32);
		    pparams->viewport[3] -= YRES(32 * 2);
        
        }

		if ( gHUD.m_Spectator.IsInOverviewMode())
        {
			pparams->nextView = 1;	// force a second renderer view
        }

		gHUD.m_Spectator.m_iDrawCycle = 0;

	}
	else
	{
		// second renderer cycle, inset window

		// set inset parameters
		pparams->viewport[0] = XRES(gHUD.m_Spectator.m_OverviewData.insetWindowX) + 1;	// change viewport to inset window
		pparams->viewport[1] = YRES(gHUD.m_Spectator.m_OverviewData.insetWindowY) + 1;
		pparams->viewport[2] = XRES(gHUD.m_Spectator.m_OverviewData.insetWindowWidth) - 2;
		pparams->viewport[3] = YRES(gHUD.m_Spectator.m_OverviewData.insetWindowHeight) - 2;
		pparams->nextView	 = 0;	// on further view
		pparams->onlyClientDraw = false;

        if (gHUD.m_Spectator.IsInOverviewMode())
        {
            if (g_iUser1 == OBS_IN_EYE)
            {
                V_GetInEyePos( g_iUser2, v_origin, v_angles );
            }
            else if (g_iUser1 == OBS_CHASE_FREE)
            {
                V_GetChasePos( g_iUser2, v_cl_angles, v_origin, v_angles );
            }
            else if (g_iUser1 == OBS_CHASE_LOCKED)
            {
                V_GetChasePos( g_iUser2, NULL, v_origin, v_angles );
            }
            else if (g_iUser1 == OBS_ROAMING)
            {
                VectorCopy (v_cl_angles, v_angles);
                VectorCopy (v_sim_org, v_origin);
            }
        }

        // Removed by mmcguire.
        /*
		// override some settings in certain modes
		switch ( (int)gHUD.m_Spectator.m_pip->value )
		{
            case INSET_CHASE_FREE : V_GetChasePos( g_iUser2, v_cl_angles, v_origin, v_angles );
                                    break;

            case INSET_CHASE_LOCKED : V_GetChasePos( g_iUser2, NULL, v_origin, v_angles );
									break;	

			case INSET_IN_EYE	 :	V_GetInEyePos( g_iUser2, v_origin, v_angles );
									break;

			case INSET_MAP_FREE  :	pparams->onlyClientDraw = true;
									V_GetMapFreePosition( v_cl_angles, v_origin, v_angles );
									break;

			case INSET_MAP_CHASE  :	pparams->onlyClientDraw = true;

									if ( g_iUser1 == OBS_ROAMING )
										V_GetMapChasePosition( 0, v_cl_angles, v_origin, v_angles );
									else
										V_GetMapChasePosition( g_iUser2, v_cl_angles, v_origin, v_angles );

									break;
		}
        */

		gHUD.m_Spectator.m_iDrawCycle = 1;
	}


	// do the smoothing only once per frame, not in roaming or map mode
	if ( (gHUD.m_Spectator.m_iDrawCycle == 0) && (g_iUser1 == OBS_IN_EYE)  )
	{
		// smooth angles

		VectorSubtract( v_angles, lastang, delta );
		if ( Length( delta ) != 0.0f )
		{
			VectorCopy( v_angles, ViewInterp.Angles[ ViewInterp.CurrentAngle & ORIGIN_MASK ] );
			ViewInterp.AngleTime[ ViewInterp.CurrentAngle & ORIGIN_MASK ] = pparams->time;
			ViewInterp.CurrentAngle++;
			VectorCopy( v_angles, lastang );
		}

		if ( cl_vsmoothing && cl_vsmoothing->value )
		{
			int foundidx;
			int i;
			float t;

			t = pparams->time - cl_vsmoothing->value;

			for ( i = 1; i < ORIGIN_MASK; i++ )
			{
				foundidx = ViewInterp.CurrentAngle - 1 - i;
				if ( ViewInterp.AngleTime[ foundidx & ORIGIN_MASK ] <= t )
					break;
			}
			
			if ( i < ORIGIN_MASK && ViewInterp.AngleTime[ foundidx & ORIGIN_MASK ] != 0.0 )
			{
				// Interpolate
				double dt;
				float  da;
				vec3_t	v1,v2;

				AngleVectors( ViewInterp.Angles[ foundidx & ORIGIN_MASK ], v1, NULL, NULL );
				AngleVectors( ViewInterp.Angles[ (foundidx + 1) & ORIGIN_MASK ], v2, NULL, NULL );
				da = AngleBetweenVectors( v1, v2 );

				dt = ViewInterp.AngleTime[ (foundidx + 1) & ORIGIN_MASK ] - ViewInterp.AngleTime[ foundidx & ORIGIN_MASK ];
					
				if ( dt > 0.0 && ( da < 22.5f) )
				{
					double frac;

					frac = ( t - ViewInterp.AngleTime[ foundidx & ORIGIN_MASK] ) / dt;
					frac = min( 1.0, frac );

					// interpolate angles
					InterpolateAngles( ViewInterp.Angles[ foundidx & ORIGIN_MASK ], ViewInterp.Angles[ (foundidx + 1) & ORIGIN_MASK ], v_angles, frac );
				}
			}
		} 

  		// smooth origin
		
		VectorSubtract( v_origin, lastorg, delta );

		if ( Length( delta ) != 0.0 )
		{
			VectorCopy( v_origin, ViewInterp.Origins[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] );
			ViewInterp.OriginTime[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] = pparams->time;
			ViewInterp.CurrentOrigin++;

			VectorCopy( v_origin, lastorg );
		}

		// don't smooth in roaming (already smoothd), 
		if ( cl_vsmoothing && cl_vsmoothing->value  )
		{
			int foundidx;
			int i;
			float t;

			t = pparams->time - cl_vsmoothing->value;

			for ( i = 1; i < ORIGIN_MASK; i++ )
			{
				foundidx = ViewInterp.CurrentOrigin - 1 - i;
				if ( ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] <= t )
					break;
			}

			if ( i < ORIGIN_MASK &&  ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] != 0.0 )
			{
				// Interpolate
				vec3_t delta;
				double frac;
				double dt;
				vec3_t neworg;

				dt = ViewInterp.OriginTime[ (foundidx + 1) & ORIGIN_MASK ] - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ];
				if ( dt > 0.0 )
				{
					frac = ( t - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK] ) / dt;
					frac = min( 1.0, frac );
					VectorSubtract( ViewInterp.Origins[ ( foundidx + 1 ) & ORIGIN_MASK ], ViewInterp.Origins[ foundidx & ORIGIN_MASK ], delta );
					VectorMA( ViewInterp.Origins[ foundidx & ORIGIN_MASK ], frac, delta, neworg );

					// Dont interpolate large changes
					if ( Length( delta ) < 64 )
					{
						VectorCopy( neworg, v_origin );
					}
				}
			}
		}
	}	

	// Hack in weapon model:
	cl_entity_t* ent = gEngfuncs.GetEntityByIndex(g_iUser2);
	cl_entity_t* gunModel = gEngfuncs.GetViewModel();
	if ( (g_iUser1 == OBS_IN_EYE) && ent && g_iUser2 )
	{
		// get position for weapon model
		VectorCopy( v_origin, gunModel->origin);
		VectorCopy( v_angles, gunModel->angles);

		// add idle tremble
		gunModel->angles[PITCH]*=-1;
		
		// calculate player velocity
		float timeDiff = ent->curstate.msg_time - ent->prevstate.msg_time;

		if ( timeDiff > 0 )
		{
			vec3_t distance;
			VectorSubtract(ent->prevstate.origin, ent->curstate.origin, distance);
			VectorScale(distance, 1/timeDiff, distance );

			velocity[0] = velocity[0]*0.66f + distance[0]*0.33f;
			velocity[1] = velocity[1]*0.66f + distance[1]*0.33f;
			velocity[2] = velocity[2]*0.66f + distance[2]*0.33f;
			
			VectorCopy(velocity, pparams->simvel);
			pparams->onground = 1;

			bob = V_CalcBob( pparams );
		}

		vec3_t forward;
		AngleVectors(v_angles, forward, NULL, NULL );

		for ( int i = 0; i < 3; i++ )
		{
			gunModel->origin[ i ] += bob * 0.4 * forward[ i ];
		}
		
		// throw in a little tilt.
		gunModel->angles[YAW]   -= bob * 0.5;
		gunModel->angles[ROLL]  -= bob * 1;
		gunModel->angles[PITCH] -= bob * 0.3;

		VectorCopy( gunModel->angles, gunModel->curstate.angles );
		VectorCopy( gunModel->angles, gunModel->latched.prevangles );

		lastWeaponModelIndex = ent->curstate.weaponmodel;
		int theCurrentViewModel = V_FindViewModelByWeaponModel( lastWeaponModelIndex, ent->curstate.iuser3, ent->curstate.iuser4);

		//if ( lastWeaponModelIndex != ent->curstate.weaponmodel )
		if ( lastViewModelIndex != theCurrentViewModel )
		{
			// weapon model changed
			lastViewModelIndex = theCurrentViewModel;
			if ( lastViewModelIndex )
			{
				gEngfuncs.pfnWeaponAnim(0,0);	// reset weapon animation
			}
			else
			{
				// model not found
				gunModel->model = NULL;	// disable weaopn model
				lastWeaponModelIndex = lastViewModelIndex = 0;
			}
		}

		if ( lastViewModelIndex )
		{
			gunModel->model = IEngineStudio.GetModelByIndex( lastViewModelIndex );
			gunModel->curstate.modelindex = lastViewModelIndex;
			gunModel->curstate.frame = 0;
			gunModel->curstate.colormap = 0; 
			gunModel->index = g_iUser2;
		}
		else
		{
			gunModel->model = NULL;	// disable weaopn model
		}
	}
	else
	{
		gunModel->model = NULL;	// disable weaopn model
		lastWeaponModelIndex = lastViewModelIndex = 0;
	}

	lasttime = pparams->time; 

	// write back new values into pparams

	VectorCopy ( v_angles, pparams->viewangles )
	VectorCopy ( v_origin, pparams->vieworg );

}

void CL_DLLEXPORT V_CalcRefdef( struct ref_params_s *pparams )
{
	RecClCalcRefdef(pparams);
	
    if (!pparams->hardware)
    {
        // Don't show anything in software mode.
        pparams->onlyClientDraw = true;
        pparams->viewport[0] = 0;
        pparams->viewport[1] = 0;
        pparams->viewport[2] = 0;
        pparams->viewport[3] = 0;
    }
    else
    {

        // intermission / finale rendering
	    if ( pparams->intermission )
	    {	
		    V_CalcIntermissionRefdef ( pparams );	
	    }
	    else if ( pparams->spectator || g_iUser1) // g_iUser1 true if in spectator mode
	    {
		    V_CalcSpectatorRefdef ( pparams );	
	    }
	    else if ( !pparams->paused )
	    {
		    if(gHUD.GetInTopDownMode())
		    {
			    V_CalcTopDownRefdef ( pparams );
		    }
		    else
		    {
			    V_CalcNormalRefdef ( pparams );
		    }
	    }
    
    }

    gHUD.SetViewport(pparams->viewport);

//// Example of how to overlay the whole screen with red at 50 % alpha
//#define SF_TEST
//#if defined SF_TEST
//	{
//		screenfade_t sf;
//		gEngfuncs.pfnGetScreenFade( &sf );
//
//		sf.fader = 0;
//		sf.fadeg = 255;
//		sf.fadeb = 0;
//		sf.fadealpha = 60;
//		sf.fadeFlags = FFADE_STAYOUT | FFADE_OUT;
//
//		gEngfuncs.pfnSetScreenFade( &sf );
//	}
//#endif
}

/*
=============
V_DropPunchAngle

=============
*/
void V_DropPunchAngle ( float frametime, float *ev_punchangle )
{
	float	len;
	
	len = VectorNormalize ( ev_punchangle );
	len -= (10.0 + len * 0.5) * frametime;
	len = max( len, 0.0 );
	VectorScale ( ev_punchangle, len, ev_punchangle );
}

/*
=============
V_PunchAxis

Client side punch effect
=============
*/
void V_PunchAxis( int axis, float punch )
{
	ev_punchangle[ axis ] = punch;
}

/*
=============
V_Init
=============
*/
void V_Init (void)
{
	gEngfuncs.pfnAddCommand ("centerview", V_StartPitchDrift );

	scr_ofsx			= gEngfuncs.pfnRegisterVariable( "scr_ofsx","0", 0 );
	scr_ofsy			= gEngfuncs.pfnRegisterVariable( "scr_ofsy","0", 0 );
	scr_ofsz			= gEngfuncs.pfnRegisterVariable( "scr_ofsz","0", 0 );

	v_centermove		= gEngfuncs.pfnRegisterVariable( "v_centermove", "0.15", 0 );
	v_centerspeed		= gEngfuncs.pfnRegisterVariable( "v_centerspeed","500", 0 );

	cl_bobcycle			= gEngfuncs.pfnRegisterVariable( "cl_bobcycle","0.8", 0 );// best default for my experimental gun wag (sjb)
	cl_bob				= gEngfuncs.pfnRegisterVariable( "cl_bob","0.01", 0 );// best default for my experimental gun wag (sjb)
	cl_bobup			= gEngfuncs.pfnRegisterVariable( "cl_bobup","0.5", 0 );
	cl_waterdist		= gEngfuncs.pfnRegisterVariable( "cl_waterdist","4", 0 );
	cl_hudcam			= gEngfuncs.pfnRegisterVariable( "cl_hudcam", "1", 0 );
	cl_chasedist		= gEngfuncs.pfnRegisterVariable( "cl_chasedist", "200", 0 );
}


//#define TRACE_TEST
#if defined( TRACE_TEST )

extern float in_fov;
/*
====================
CalcFov
====================
*/
float CalcFov (float fov_x, float width, float height)
{
	float	a;
	float	x;

	if (fov_x < 1 || fov_x > 179)
		fov_x = 90;	// error, set to 90

	x = width/tan(fov_x/360*M_PI);

	a = atan (height/x);

	a = a*360/M_PI;

	return a;
}

int hitent = -1;

void V_Move( int mx, int my )
{
	float fov;
	float fx, fy;
	float dx, dy;
	float c_x, c_y;
	float dX, dY;
	vec3_t forward, up, right;
	vec3_t newangles;

	vec3_t farpoint;
	pmtrace_t tr;

	fov = CalcFov( in_fov, (float)ScreenWidth, (float)ScreenHeight );

	c_x = (float)ScreenWidth / 2.0;
	c_y = (float)ScreenHeight / 2.0;

	dx = (float)mx - c_x;
	dy = (float)my - c_y;

	// Proportion we moved in each direction
	fx = dx / c_x;
	fy = dy / c_y;

	dX = fx * in_fov / 2.0 ;
	dY = fy * fov / 2.0;

	newangles = v_angles;

	newangles[ YAW ] -= dX;
	newangles[ PITCH ] += dY;

	// Now rotate v_forward around that point
	AngleVectors ( newangles, forward, right, up );

	farpoint = v_origin + 8192 * forward;

	// Trace
	tr = *(gEngfuncs.PM_TraceLine( (float *)&v_origin, (float *)&farpoint, PM_TRACELINE_PHYSENTSONLY, 2 /*point sized hull*/, -1 ));

	if ( tr.fraction != 1.0 && tr.ent != 0 )
	{
		hitent = PM_GetPhysEntInfo( tr.ent );
		PM_ParticleLine( (float *)&v_origin, (float *)&tr.endpos, 5, 1.0, 0.0 );
	}
	else
	{
		hitent = -1;
	}
}

#endif




///*
//==================
//V_CalcTopDownRefdef
//
//==================
//*/
//void V_CalcTopDownRefdef ( struct ref_params_s *pparams )
//{
//	cl_entity_t		*ent, *view;
//	int				i;
//	vec3_t			angles;
//	static viewinterp_t		ViewInterp;
//
//	static float oldz = 0;
//	static float lasttime;
//
//	static float lastang[3];
//	vec3_t angdelta;
//
//	vec3_t camAngles, camForward, camRight, camUp;
//	//cl_entity_t *pwater;
//
//	// don't allow cheats in multiplayer
//	if ( pparams->maxclients > 1 )
//	{
//		scr_ofsx->value = 0.0;
//		scr_ofsy->value = 0.0;
//		scr_ofsz->value = 0.0;
//	}
//
//
//	V_DriftPitch ( pparams );
//
//	// ent is the player model ( visible when out of body )
//	ent = gEngfuncs.GetLocalPlayer();
//	
//	// view is the weapon model (only visible from inside body )
//	view = gEngfuncs.GetViewModel();
//
//	// Observer angle capturing and smoothing
//	if ( iHasNewViewOrigin )
//	{
//		// Get the angles from the physics code
//		VectorCopy( gTopDownViewOrigin, pparams->vieworg );
//		VectorCopy( gTopDownViewOrigin, pparams->simorg );
//	}
//
//	// Override position to commander position
//	pparams->vieworg[2] = gTopDownHeight;
//	pparams->simorg[2] = gTopDownHeight;
//
//	// Override view height when in top down
//	pparams->viewheight[2] = 0.0f;
//	
//	// refresh position
//	VectorCopy ( pparams->simorg, pparams->vieworg );
//	VectorAdd( pparams->vieworg, pparams->viewheight, pparams->vieworg );
//
//	// Observer angle capturing and smoothing
//	if ( iHasNewViewAngles )
//	{
//		// Get the angles from the physics code
//		VectorCopy( gTopDownViewAngles, pparams->cl_viewangles );
//	}
//
//	VectorSubtract( pparams->cl_viewangles, lastang, angdelta );
//	if ( Length( angdelta ) != 0.0 )
//	{
//		VectorCopy( pparams->cl_viewangles, ViewInterp.Angles[ ViewInterp.CurrentAngle & ORIGIN_MASK ] );
//		ViewInterp.AngleTime[ ViewInterp.CurrentAngle & ORIGIN_MASK ] = pparams->time;
//		ViewInterp.CurrentAngle++;
//
//		VectorCopy( pparams->cl_viewangles, lastang );
//	}
//
//	if ( cl_vsmoothing && cl_vsmoothing->value && ( iIsSpectator & SPEC_SMOOTH_ANGLES ) )
//	{
//		int foundidx;
//		int i;
//		float t;
//
//		if ( cl_vsmoothing->value < 0.0 )
//		{
//			gEngfuncs.Cvar_SetValue( "cl_vsmoothing", 0.0 );
//		}
//
//		t = pparams->time - cl_vsmoothing->value;
//
//		for ( i = 1; i < ORIGIN_MASK; i++ )
//		{
//			foundidx = ViewInterp.CurrentAngle - 1 - i;
//			if ( ViewInterp.AngleTime[ foundidx & ORIGIN_MASK ] <= t )
//				break;
//		}
//
//		if ( i < ORIGIN_MASK && ViewInterp.AngleTime[ foundidx & ORIGIN_MASK ] != 0.0 )
//		{
//			// Interpolate
//			double dt;
//
//			dt = ViewInterp.AngleTime[ (foundidx + 1) & ORIGIN_MASK ] - ViewInterp.AngleTime[ foundidx & ORIGIN_MASK ];
//			if ( dt > 0.0 )
//			{
//				double frac;
//
//				frac = ( t - ViewInterp.AngleTime[ foundidx & ORIGIN_MASK] ) / dt;
//				frac = min( 1.0, frac );
//
//				// interpolate angles
//				InterpolateAngles( ViewInterp.Angles[ foundidx & ORIGIN_MASK ], ViewInterp.Angles[ (foundidx + 1) & ORIGIN_MASK ], pparams->cl_viewangles, frac );
//
//				VectorCopy( pparams->cl_viewangles, gTopDownViewAngles );
//			}
//		}
//	}
//
//
//	VectorCopy ( pparams->cl_viewangles, pparams->viewangles );
//
//	gEngfuncs.V_CalcShake();
//	gEngfuncs.V_ApplyShake( pparams->vieworg, pparams->viewangles, 1.0 );
//
////	// never let view origin sit exactly on a node line, because a water plane can
////	// dissapear when viewed with the eye exactly on it.
////	// FIXME, we send origin at 1/128 now, change this?
////	// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
////	
////	pparams->vieworg[0] += 1.0/32;
////	pparams->vieworg[1] += 1.0/32;
////	pparams->vieworg[2] += 1.0/32;
//
//	// Check for problems around water, move the viewer artificially if necessary 
//	// -- this prevents drawing errors in GL due to waves
//
////	if ( pparams->waterlevel >= 2 )
////	{
////		int		waterDist, waterEntity;
////		vec3_t	point;
////		waterDist = cl_waterdist->value;
////
////		if ( pparams->hardware )
////		{
////			waterEntity = gEngfuncs.PM_WaterEntity( pparams->simorg );
////			if ( waterEntity >= 0 && waterEntity < pparams->max_entities )
////			{
////				pwater = gEngfuncs.GetEntityByIndex( waterEntity );
////				if ( pwater && ( pwater->model != NULL ) )
////				{
////					waterDist += ( pwater->curstate.scale * 16 );	// Add in wave height
////				}
////			}
////		}
////		else
////		{
////			waterEntity = 0;	// Don't need this in software
////		}
////		
////		VectorCopy( pparams->vieworg, point );
////	}
//
//	V_CalcViewRoll ( pparams );
//	
//	//V_AddIdle ( pparams );
//
//	// offsets
//	VectorCopy( pparams->cl_viewangles, angles );
//
//	AngleVectors ( angles, pparams->forward, pparams->right, pparams->up );
//
//	for ( i=0 ; i<3 ; i++ )
//	{
//		pparams->vieworg[i] += scr_ofsx->value*pparams->forward[i] + scr_ofsy->value*pparams->right[i] + scr_ofsz->value*pparams->up[i];
//	}
//	
//	// Treating cam_ofs[2] as the distance
////	if( CL_IsThirdPerson() )
////	{
////		vec3_t ofs;
////
////		ofs[0] = ofs[1] = ofs[2] = 0.0;
////
////		CL_CameraOffset( (float *)&ofs );
////
////		VectorCopy( ofs, camAngles );
////		camAngles[ ROLL ]	= 0;
////
////		AngleVectors( camAngles, camForward, camRight, camUp );
////
////		for ( i = 0; i < 3; i++ )
////		{
////			pparams->vieworg[ i ] += -ofs[2] * camForward[ i ];
////		}
////	}
//	
//	// Give gun our viewangles
////	VectorCopy ( pparams->cl_viewangles, view->angles );
//	
//	// set up gun position
////	V_CalcGunAngle ( pparams );
//
//	// Use predicted origin as view origin.
//	VectorCopy ( pparams->simorg, view->origin );      
//	VectorAdd( view->origin, pparams->viewheight, view->origin );
//
//	// Let the viewmodel shake at about 10% of the amplitude
////	gEngfuncs.V_ApplyShake( view->origin, view->angles, 0.9 );
//
//	// smooth out stair step ups
//#if 1
//	if ( !pparams->smoothing && pparams->onground && pparams->simorg[2] - oldz > 0)
//	{
//		float steptime;
//		
//		steptime = pparams->time - lasttime;
//		if (steptime < 0)
//	//FIXME		I_Error ("steptime < 0");
//			steptime = 0;
//
//		oldz += steptime * 150;
//		if (oldz > pparams->simorg[2])
//			oldz = pparams->simorg[2];
//		if (pparams->simorg[2] - oldz > 18)
//			oldz = pparams->simorg[2]- 18;
//		pparams->vieworg[2] += oldz - pparams->simorg[2];
//		view->origin[2] += oldz - pparams->simorg[2];
//	}
//	else
//	{
//		oldz = pparams->simorg[2];
//	}
//#endif
//
//	{
//		static float lastorg[3];
//		vec3_t delta;
//
//		VectorSubtract( pparams->simorg, lastorg, delta );
//
//		if ( Length( delta ) != 0.0 )
//		{
//			VectorCopy( pparams->simorg, ViewInterp.Origins[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] );
//			ViewInterp.OriginTime[ ViewInterp.CurrentOrigin & ORIGIN_MASK ] = pparams->time;
//			ViewInterp.CurrentOrigin++;
//
//			VectorCopy( pparams->simorg, lastorg );
//		}
//	}
//
//	// Smooth out whole view in multiplayer when on trains, lifts
//	if ( cl_vsmoothing && cl_vsmoothing->value &&
//		( ( iIsSpectator & SPEC_SMOOTH_ORIGIN ) || (pparams->smoothing && ( pparams->maxclients > 1 ) ) ) )
//	{
//		int foundidx;
//		int i;
//		float t;
//
//		if ( cl_vsmoothing->value < 0.0 )
//		{
//			gEngfuncs.Cvar_SetValue( "cl_vsmoothing", 0.0 );
//		}
//
//		t = pparams->time - cl_vsmoothing->value;
//
//		for ( i = 1; i < ORIGIN_MASK; i++ )
//		{
//			foundidx = ViewInterp.CurrentOrigin - 1 - i;
//			if ( ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] <= t )
//				break;
//		}
//
//		if ( i < ORIGIN_MASK &&  ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ] != 0.0 )
//		{
//			// Interpolate
//			vec3_t delta;
//			double frac;
//			double dt;
//			vec3_t neworg;
//
//			dt = ViewInterp.OriginTime[ (foundidx + 1) & ORIGIN_MASK ] - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK ];
//			if ( dt > 0.0 )
//			{
//				frac = ( t - ViewInterp.OriginTime[ foundidx & ORIGIN_MASK] ) / dt;
//				frac = min( 1.0, frac );
//				VectorSubtract( ViewInterp.Origins[ ( foundidx + 1 ) & ORIGIN_MASK ], ViewInterp.Origins[ foundidx & ORIGIN_MASK ], delta );
//				VectorMA( ViewInterp.Origins[ foundidx & ORIGIN_MASK ], frac, delta, neworg );
//
//				// Dont interpolate large changes
//				if ( Length( delta ) < 64 )
//				{
//					VectorSubtract( neworg, pparams->simorg, delta );
//
//					VectorAdd( pparams->simorg, delta, pparams->simorg );
//					VectorAdd( pparams->vieworg, delta, pparams->vieworg );
//					VectorAdd( view->origin, delta, view->origin );
//
//					VectorCopy( pparams->simorg, gTopDownViewOrigin );
//				}
//			}
//		}
//	}
//
//	// Store off v_angles before munging for third person
//	v_angles = pparams->viewangles;
//
//	// override all previous settings if the viewent isn't the client
//	if ( pparams->viewentity > pparams->maxclients )
//	{
//		cl_entity_t *viewentity;
//		viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity );
//		if ( viewentity )
//		{
//			VectorCopy( viewentity->origin, pparams->vieworg );
//			VectorCopy( viewentity->angles, pparams->viewangles );
//
//			// Store off overridden viewangles
//			v_angles = pparams->viewangles;
//		}
//	}
//
//	lasttime = pparams->time;
//
//	v_origin = pparams->vieworg;
//}