// 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 "bench.h" #include "screenfade.h" #include "engine/shake.h" #include "mod/AvHClientUtil.h" #include "engine/APIProxy.h" #include "hltv.h" #include "Exports.h" #include "common/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 int CL_IsThirdPerson( void ); void CL_CameraOffset( float *ofs ); void CL_DLLEXPORT V_CalcRefdef( struct ref_params_s *pparams ); void PM_ParticleLine( float *start, float *end, int pcolor, float life, float vert); int PM_GetVisEntInfo( int ent ); int PM_GetPhysEntInfo( int ent ); //@linux void InterpolateAngles( float * start, float * end, float * output, float frac ); void NormalizeAngles( float * angles ); //extern "C" float Distance(const float * v1, const float * v2); //float AngleBetweenVectors(const float * v1, const 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 "common/com_model.h" #include "kbutton.h" extern engine_studio_api_t IEngineStudio; extern kbutton_t in_mlook; /* 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 *cl_forwardspeed; extern cvar_t *chase_active; extern cvar_t *scr_ofsx, *scr_ofsy, *scr_ofsz; extern cvar_t *cl_vsmoothing; extern cvar_t *cl_bobview; #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 //@linux void 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; } } } /* =================== 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 InterpolateAngles( float *start, float *end, float *output, float frac ) { int i; float ang1, ang2; float d; NormalizeAngles( start ); 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; } NormalizeAngles( output ); } /* =================== AngleBetweenVectors =================== */ float AngleBetweenVectors( float* v1, float* v2 ) { float angle; float l1 = Length( v1 ); float l2 = Length( v2 ); if ( !l1 || !l2 ) return 0.0f; angle = acos( DotProduct( v1, v2 ) ) / (l1*l2); angle = ( angle * 180.0f ) / M_PI; return angle; } //============================================================================= /* 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 ( v_centermove->value > 0 && !(in_mlook.state & 1) ) { // this is for lazy players. if they stopped, looked around and then continued // to move the view will be centered automatically if they move more than // v_centermove units. if ( fabs( pparams->cmd->forwardmove ) < kForwardSpeed ) pd.driftmove = 0; else pd.driftmove += pparams->frametime; if ( pd.driftmove > v_centermove->value) { V_StartPitchDrift (); } else { return; // player didn't move enough } } return; // don't drift view } 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; pd.pitchvel *= (1.0f+(pparams->frametime*0.25f)); // get faster by time 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 ); if (cl_bobview && cl_bobview->value != 0) { 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 ]; } if (cl_bobview && cl_bobview->value != 0) { 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 ); 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]; } } // 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, const 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, const 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 target, float * origin, float * angles ) { if ( !target) { // just copy a save in-map position VectorCopy ( vJumpAngles, angles ); VectorCopy ( vJumpOrigin, origin ); return; }; cl_entity_t * ent = gEngfuncs.GetEntityByIndex( target ); if ( !ent ) return; VectorCopy ( ent->origin, origin ); VectorCopy ( ent->angles, angles ); angles[PITCH]*=-3.0f; // see CL_ProcessEntityUpdate() 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_cl_angles, pparams->cl_viewangles ); VectorCopy ( v_angles, pparams->viewangles ) VectorCopy ( v_origin, pparams->vieworg ); } void CL_DLLEXPORT V_CalcRefdef( struct ref_params_s *pparams ) { // RecClCalcRefdef(pparams); // intermission / finale rendering if ( pparams->intermission ) { V_CalcIntermissionRefdef ( pparams ); } else if ( pparams->spectator || g_iUser1 ) // g_iUser true if in spectator mode { V_CalcSpectatorRefdef ( pparams ); } else if ( !pparams->paused ) { if(gHUD.GetInTopDownMode()) { V_CalcTopDownRefdef ( pparams ); } else { V_CalcNormalRefdef ( pparams ); } } /* // 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 = 255; sf.fadeg = 0; sf.fadeb = 0; sf.fadealpha = 128; 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 ); // lightgamma doesn't save to config.cfg otherwise. cvar_t* lightgamma = gEngfuncs.pfnGetCvarPointer("lightgamma"); lightgamma->flags |= FCVAR_ARCHIVE; } //#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; extern playermove_t *pmove; void PM_ParticleLine(vec3_t start, vec3_t end, int pcolor, float life, float vert) { float linestep = 2.0f; float curdist; float len; vec3_t curpos; vec3_t diff; int i; // Determine distance; VectorSubtract(end, start, diff); len = VectorNormalize(diff); curdist = 0; while (curdist <= len) { for (i = 0; i < 3; i++) curpos[i] = start[i] + curdist * diff[i]; pmove->PM_Particle( curpos, pcolor, life, 0, vert); curdist += linestep; } } 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( (vec3_t)v_origin, (vec3_t)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; //}