//======================================================================= // Copyright XashXT Group 2008 © // view.cpp - view/refresh setup functions //======================================================================= #include "extdll.h" #include "utils.h" #include "ref_params.h" #include "triangle_api.h" #include "pm_movevars.h" #include "studio_ref.h" #include "user_cmd.h" #include "hud.h" #define ORIGIN_BACKUP 64 #define ORIGIN_MASK ( ORIGIN_BACKUP - 1 ) // global view containers Vector v_origin, v_angles, v_cl_angles; // base client vectors float v_idlescale, v_lastDistance; // misc variables Vector ev_punchangle; // client punchangles edict_t *spot; typedef struct { Vector Origins[ORIGIN_BACKUP]; float OriginTime[ORIGIN_BACKUP]; Vector Angles[ORIGIN_BACKUP]; float AngleTime[ORIGIN_BACKUP]; int CurrentOrigin; int CurrentAngle; } viewinterp_t; typedef struct pitchdrift_s { int nodrift; float pitchvel; float driftmove; float laststop; } pitchdrift_t; static viewinterp_t ViewInterp; static pitchdrift_t pd; // view CVARS cvar_t *scr_ofsx; cvar_t *scr_ofsy; cvar_t *scr_ofsz; cvar_t *cl_vsmoothing; cvar_t *cl_forwardspeed; cvar_t *r_mirrors; cvar_t *r_portals; cvar_t *r_screens; cvar_t *r_debug; cvar_t *cl_bobcycle; cvar_t *cl_bob; cvar_t *cl_bobup; cvar_t *cl_waterdist; cvar_t *cl_chasedist; cvar_t *v_centermove; cvar_t *v_centerspeed; cvar_t *v_iyaw_cycle; cvar_t *v_iroll_cycle; cvar_t *v_ipitch_cycle; cvar_t *v_iyaw_level; cvar_t *v_iroll_level; cvar_t *v_ipitch_level; //============================================================================== // PASS MANAGER GLOBALS //============================================================================== // render manager global variables bool g_FirstFrame = false; bool g_RenderReady = false; bool g_bFinalPass = false; bool g_bEndCalc = false; int m_RenderRefCount = 0; // refcounter (use for debug) // passes info bool g_bMirrorShouldpass = false; bool g_bScreenShouldpass = false; bool g_bPortalShouldpass = false; bool g_bSkyShouldpass = false; bool g_bMirrorPass = false; bool g_bPortalPass = false; bool g_bScreenPass = false; bool g_bSkyPass = false; // rendering info int g_iTotalVisibleMirrors = 0; int g_iTotalVisibleScreens = 0; int g_iTotalVisiblePortals = 0; int g_iTotalMirrors = 0; int g_iTotalScreens = 0; int g_iTotalPortals = 0; // base origin and angles Vector g_vecBaseOrigin; // base origin - transformed always Vector g_vecBaseAngles; // base angles - transformed always Vector g_vecCurrentOrigin; // current origin Vector g_vecCurrentAngles; // current angles //============================================================================== // VIEW RENDERING //============================================================================== //========================== // V_ThirdPerson //========================== void V_ThirdPerson( void ) { // no thirdperson in multiplayer if( GetMaxClients() > 1 ) return; if( !gHUD.viewFlags ) gHUD.m_iLastCameraMode = gHUD.m_iCameraMode = 1; else gHUD.m_iLastCameraMode = 1; // set new view after release camera } //========================== // V_FirstPerson //========================== void V_FirstPerson( void ) { if( !gHUD.viewFlags ) gHUD.m_iLastCameraMode = gHUD.m_iCameraMode = 0; else gHUD.m_iLastCameraMode = 0; // set new view after release camera } //========================== // V_Init //========================== void V_Init( void ) { scr_ofsx = g_engfuncs.pfnRegisterVariable( "scr_ofsx", "0", 0, "screen offset by X" ); scr_ofsy = g_engfuncs.pfnRegisterVariable( "scr_ofsy", "0", 0, "screen offset by Y" ); scr_ofsz = g_engfuncs.pfnRegisterVariable( "scr_ofsz", "0", 0, "screen offset by Z" ); cl_vsmoothing = g_engfuncs.pfnRegisterVariable( "cl_vsmoothing", "0", 0, "enables lepring in multiplayer" ); cl_forwardspeed = g_engfuncs.pfnRegisterVariable( "cl_forwardspeed", "200", 0, "client forward speed limit" ); v_iyaw_cycle = g_engfuncs.pfnRegisterVariable( "v_iyaw_cycle", "2", 0, "viewing inverse yaw cycle" ); v_iroll_cycle = g_engfuncs.pfnRegisterVariable( "v_iroll_cycle", "0.5", 0, "viewing inverse roll cycle" ); v_ipitch_cycle = g_engfuncs.pfnRegisterVariable( "v_ipitch_cycle", "1", 0, "viewing inverse pitch cycle" ); v_iyaw_level = g_engfuncs.pfnRegisterVariable( "v_iyaw_level", "0.3", 0, "viewing inverse yaw level" ); v_iroll_level = g_engfuncs.pfnRegisterVariable( "v_iroll_level", "0.1", 0, "viewing inverse roll level" ); v_ipitch_level = g_engfuncs.pfnRegisterVariable( "v_iyaw_level", "0.3", 0, "viewing inverse pitch level" ); v_centermove = g_engfuncs.pfnRegisterVariable( "v_centermove", "0.15", 0, "center moving scale" ); v_centerspeed = g_engfuncs.pfnRegisterVariable( "v_centerspeed", "500", 0, "center moving speed" ); cl_bobcycle = g_engfuncs.pfnRegisterVariable( "cl_bobcycle","0.8", 0, "bob full cycle" ); cl_bob = g_engfuncs.pfnRegisterVariable( "cl_bob", "0.01", 0, "bob value" ); cl_bobup = g_engfuncs.pfnRegisterVariable( "cl_bobup", "0.5", 0, "bob upper limit" ); cl_waterdist = g_engfuncs.pfnRegisterVariable( "cl_waterdist", "4", 0, "distance between viewofs and water plane" ); cl_chasedist = g_engfuncs.pfnRegisterVariable( "cl_chasedist", "112", 0, "max distance to chase camera" ); g_engfuncs.pfnAddCommand( "thirdperson", V_ThirdPerson, "change camera to thirdperson" ); g_engfuncs.pfnAddCommand( "firstperson", V_FirstPerson, "change camera to firstperson" ); r_mirrors = g_engfuncs.pfnRegisterVariable( "r_mirrors", "0", FCVAR_ARCHIVE, "enable mirrors rendering" ); r_portals = g_engfuncs.pfnRegisterVariable( "r_portals", "0", FCVAR_ARCHIVE, "enable portals rendering" ); r_screens = g_engfuncs.pfnRegisterVariable( "r_screens", "0", FCVAR_ARCHIVE, "enable screens rendering" ); r_debug = g_engfuncs.pfnRegisterVariable( "r_debug", "0", 0, "show renderer state" ); } //========================== // V_StartPitchDrift //========================== void V_StartPitchDrift( void ) { if( pd.laststop == GetClientTime()) return; if( pd.nodrift || !pd.pitchvel ) { pd.pitchvel = v_centerspeed->value; pd.nodrift = 0; pd.driftmove = 0; } } //========================== // V_StopPitchDrift //========================== void V_StopPitchDrift( void ) { pd.laststop = GetClientTime(); pd.nodrift = 1; pd.pitchvel = 0; } //========================== // V_DriftPitch //========================== void V_DriftPitch( ref_params_t *pparams ) { float delta, move; if( pparams->movetype == MOVETYPE_NOCLIP || !pparams->onground || pparams->demoplayback ) { pd.driftmove = 0; pd.pitchvel = 0; return; } if( pd.nodrift ) { if( fabs( (float)pparams->cmd->forwardmove ) < cl_forwardspeed->value ) 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; 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; } } //========================== // V_CalcFov //========================== float V_CalcFov( float fov_x, float width, float height ) { float fov_y, x, rad = 360.0f * M_PI; // check to avoid division by zero if( fov_x == 0 ) HOST_ERROR( "V_CalcFov: null fov!\n" ); // make sure that fov in-range fov_x = bound( 1, fov_x, 179 ); x = width / tan( fov_x / rad ); fov_y = atan2( height, x ); fov_y = (fov_y * rad); return fov_y; } //========================== // V_DropPunchAngle //========================== void V_DropPunchAngle( float frametime, Vector &ev_punchangle ) { float len; len = ev_punchangle.Length(); ev_punchangle.Normalize(); len -= (10.0 + len * 0.5) * frametime; len = max( len, 0.0 ); ev_punchangle *= len; } //========================== // V_CalcGunAngle //========================== void V_CalcGunAngle( ref_params_t *pparams ) { if( pparams->fov_x > 135 ) return; edict_t *viewent = GetViewModel(); if( !viewent->v.modelindex ) return; viewent->serialnumber = -1; // viewentity are handled with special number. don't change this viewent->v.effects |= EF_MINLIGHT; viewent->v.angles[YAW] = pparams->viewangles[YAW] + pparams->crosshairangle[YAW]; viewent->v.angles[PITCH] = pparams->viewangles[PITCH] + pparams->crosshairangle[PITCH] * 0.25; viewent->v.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->v.angles[PITCH] -= v_idlescale * sin(pparams->time * v_ipitch_cycle->value) * (v_ipitch_level->value * 0.5); viewent->v.angles[YAW] -= v_idlescale * sin(pparams->time * v_iyaw_cycle->value) * v_iyaw_level->value; } //========================== // V_PreRender //========================== void V_PreRender( ref_params_t *pparams ) { // input pparams->intermission = gHUD.m_iIntermission; if( gHUD.m_iCameraMode ) pparams->flags |= RDF_THIRDPERSON; else pparams->flags &= ~RDF_THIRDPERSON; // output gHUD.m_CrosshairAngles = pparams->crosshairangle; pparams->fov_y = V_CalcFov( pparams->fov_x, pparams->viewport[2], pparams->viewport[3] ); } //========================== // V_CalcGlobalFog //========================== void V_CalcGlobalFog( ref_params_t *pparams ) { int bOn = (pparams->waterlevel < 2) && (gHUD.m_fStartDist > 0) && (gHUD.m_fEndDist > 0); g_engfuncs.pTriAPI->Fog( gHUD.m_FogColor, gHUD.m_fStartDist, gHUD.m_fEndDist, bOn ); } //========================== // V_CalcBob //========================== float V_CalcBob( ref_params_t *pparams ) { static double bobtime; static float bob; static float lasttime; float cycle; Vector vel; if( !pparams->onground || pparams->time == lasttime ) 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 ); vel = pparams->simvel; 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_AddIdle //========================== void V_AddIdle( ref_params_t *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 //========================== void V_CalcViewRoll( ref_params_t *pparams ) { float sign, side, value; edict_t *viewentity; Vector right; viewentity = GetEntityByIndex( pparams->viewentity ); if( !viewentity ) return; if( pparams->health <= 0 && ( pparams->viewheight[2] != 0 )) { GetViewModel()->v.modelindex = 0; // clear the viewmodel pparams->viewangles[ROLL] = 80; // dead view angle return; } AngleVectors( viewentity->v.angles, NULL, right, NULL ); side = right.Dot( pparams->simvel ); sign = side < 0 ? -1 : 1; side = fabs( side ); value = pparams->movevars->rollangle; if( side < pparams->movevars->rollspeed ) side = side * value / pparams->movevars->rollspeed; else side = value; side = side * sign; pparams->viewangles[ROLL] += side; } //========================== // V_SetViewportRefdef //========================== void V_SetViewportRefdef( ref_params_t *pparams ) { pparams->viewport[0] = 0; pparams->viewport[1] = 0; pparams->viewport[2] = VIEWPORT_SIZE; pparams->viewport[3] = VIEWPORT_SIZE; } //========================== // V_KillViewportRefdef //========================== void V_KillViewportRefdef( ref_params_t *pparams ) { pparams->viewport[0] = 0; pparams->viewport[1] = 0; pparams->viewport[2] = 1; pparams->viewport[3] = 1; } //========================== // V_ResetViewportRefdef //========================== void V_ResetViewportRefdef( ref_params_t *pparams ) { pparams->viewport[0] = 0; pparams->viewport[1] = 0; pparams->viewport[2] = ActualWidth; pparams->viewport[2] &= ~7; pparams->viewport[3] = ActualHeight; pparams->viewport[3] &= ~1; } //========================== // V_GetChaseOrigin //========================== void V_GetChaseOrigin( Vector angles, Vector origin, float distance, Vector &result ) { Vector vecEnd; Vector forward; Vector vecStart; TraceResult tr; int maxLoops = 8; edict_t *ent = NULL; edict_t *ignoreent = NULL; // trace back from the target using the player's view angles AngleVectors( angles, forward, NULL, NULL ); forward *= -1; vecStart = origin; vecEnd.MA( distance, vecStart, forward ); while( maxLoops > 0 ) { g_engfuncs.pfnTraceLine( vecStart, vecEnd, true, ignoreent, &tr ); if( tr.pHit == NULL ) break; // we hit the world or nothing, stop trace ent = tr.pHit; // hit non-player solid BSP, stop here if( ent->v.solid == SOLID_BSP && !( ent->v.flags & FL_CLIENT )) break; // if close enought to end pos, stop, otherwise continue trace if( tr.vecEndPos.Distance( vecEnd ) < 1.0f ) break; else { ignoreent = tr.pHit; // ignore last hit entity vecStart = tr.vecEndPos; } maxLoops--; } result.MA( 4, tr.vecEndPos, tr.vecPlaneNormal ); v_lastDistance = tr.vecEndPos.Distance( origin ); // real distance without offset } //========================== // V_GetChasePos //========================== void V_GetChasePos( ref_params_t *pparams, edict_t *ent, float *cl_angles, Vector &origin, Vector &angles ) { if( !ent ) { // just copy a save in-map position angles = Vector( 0, 0, 0 ); origin = Vector( 0, 0, 0 ); return; } if( cl_angles == NULL ) { // no mouse angles given, use entity angles ( locked mode ) angles = ent->v.angles; angles.x *= -1; } else angles = cl_angles; // refresh the position if( !pparams->smoothing || pparams->demoplayback ) { // use interpolated values origin = pparams->simorg + pparams->viewheight; } origin[2] += 28; // DEFAULT_VIEWHEIGHT - some offset V_GetChaseOrigin( angles, origin, cl_chasedist->value, origin ); } //========================== // V_CalcNextView //========================== void V_CalcNextView( ref_params_t *pparams ) { if( g_FirstFrame ) // first time not actually { g_FirstFrame = false; g_RenderReady = false; // first time in this function V_PreRender( pparams ); // this set g_RenderReady at true if all is'ok if( !g_RenderReady ) // no hardware capable? { g_bMirrorShouldpass = false; g_bScreenShouldpass = false; g_bPortalShouldpass = false; g_bSkyShouldpass = (gHUD.m_iSkyMode ? true : false); // sky must drawing always } else // all is'ok { g_bMirrorShouldpass = (g_iTotalVisibleMirrors > 0 && r_mirrors->value); g_bScreenShouldpass = (g_iTotalVisibleScreens > 0 && r_screens->value); g_bPortalShouldpass = (g_iTotalVisiblePortals > 0 && r_portals->value); g_bSkyShouldpass = (gHUD.m_iSkyMode ? true : false); } m_RenderRefCount = 0; // reset debug counter g_bMirrorPass = false; g_bScreenPass = false; g_bPortalPass = false; g_bSkyPass = false; g_bFinalPass = false; } } //========================== // V_CalcCameraRefdef //========================== void V_CalcCameraRefdef( ref_params_t *pparams ) { if( pparams->intermission ) return; // disable in intermission mode if( gHUD.viewFlags & CAMERA_ON ) { // get viewentity and monster eyeposition edict_t *viewentity = GetEntityByIndex( gHUD.viewEntityIndex ); if( viewentity ) { dstudiohdr_t *viewmonster = (dstudiohdr_t *)GetModelPtr( viewentity ); v_origin = viewentity->v.origin; // calc monster view if supposed if( gHUD.viewFlags & MONSTER_VIEW && viewmonster ) v_origin += viewmonster->eyeposition; v_angles = viewentity->v.angles; if( gHUD.viewFlags & INVERSE_X ) // inverse X coordinate v_angles[0] = -v_angles[0]; HideCrosshair( true ); // refresh position pparams->viewangles = v_angles; pparams->vieworg = v_origin; } } else HideCrosshair( false ); // show crosshair again } edict_t *V_FindIntermisionSpot( ref_params_t *pparams ) { edict_t *ent; int spotindex[16]; // max number of intermission spot int k = 0, j = 0; // found intermission points for( int i = 0; i < pparams->num_entities; i++ ) { ent = GetEntityByIndex( i ); if( ent && !stricmp( STRING( ent->v.classname ), "info_intermission" )) { if( j > 15 ) break; // spotlist is full spotindex[j] = ent->serialnumber; // save entindex j++; } } // ok, we have list of intermission spots if( j ) { if( j > 1 ) k = g_engfuncs.pfnRandomLong( 0, j - 1 ); ent = GetEntityByIndex( spotindex[k] ); } else ent = GetLocalPlayer(); // just get view from player return ent; } //========================== // V_CalcIntermisionRefdef //========================== void V_CalcIntermisionRefdef( ref_params_t *pparams ) { if( !pparams->intermission ) return; edict_t *view; float old; if( !spot ) spot = V_FindIntermisionSpot( pparams ); view = GetViewModel(); // need to lerping position ? pparams->vieworg = spot->v.origin; pparams->viewangles = spot->v.angles; view->v.modelindex = 0; // allways idle in intermission old = v_idlescale; v_idlescale = 1; V_AddIdle( pparams ); v_idlescale = old; v_cl_angles = pparams->cl_viewangles; v_origin = pparams->vieworg; v_angles = pparams->viewangles; } //========================== // V_PrintDebugInfo // FIXME: use custom text drawing ? //========================== void V_PrintDebugInfo( ref_params_t *pparams ) // for future extensions { if( !r_debug->value ) return; //show OpenGL renderer debug info ALERT( at_console, "Xash Renderer Info: "); if( m_RenderRefCount > 1 ) ALERT( at_console, "Total %d passes\n", m_RenderRefCount ); else ALERT( at_console, "Use normal view, make one pass\n" ); if( g_iTotalMirrors ) ALERT( at_console, "Visible mirrors: %d from %d\n", g_iTotalVisibleMirrors, g_iTotalMirrors ); if( g_iTotalScreens ) ALERT( at_console, "Visible screens: %d from %d\n", g_iTotalVisibleScreens, g_iTotalScreens ); if( g_iTotalPortals ) ALERT( at_console, "Visible portals: %d from %d\n", g_iTotalVisiblePortals, g_iTotalPortals ); } //========================== // V_CalcShake //========================== void V_CalcShake( void ) { float frametime; int i; float fraction, freq; if( gHUD.m_Shake.time == 0 ) return; if(( gHUD.m_flTime > gHUD.m_Shake.time ) || gHUD.m_Shake.duration <= 0 || gHUD.m_Shake.amplitude <= 0 || gHUD.m_Shake.frequency <= 0 ) { memset( &gHUD.m_Shake, 0, sizeof( gHUD.m_Shake )); return; } frametime = gHUD.m_flTimeDelta; if( gHUD.m_flTime > gHUD.m_Shake.nextShake ) { // higher frequency means we recalc the extents more often and perturb the display again gHUD.m_Shake.nextShake = gHUD.m_flTime + (1.0f / gHUD.m_Shake.frequency); // Compute random shake extents (the shake will settle down from this) for( i = 0; i < 3; i++ ) { gHUD.m_Shake.offset[i] = RANDOM_FLOAT( -gHUD.m_Shake.amplitude, gHUD.m_Shake.amplitude ); } gHUD.m_Shake.angle = RANDOM_FLOAT( -gHUD.m_Shake.amplitude * 0.25, gHUD.m_Shake.amplitude * 0.25 ); } // ramp down amplitude over duration (fraction goes from 1 to 0 linearly with slope 1/duration) fraction = ( gHUD.m_Shake.time - gHUD.m_flTime ) / gHUD.m_Shake.duration; // ramp up frequency over duration if( fraction ) { freq = (gHUD.m_Shake.frequency / fraction); } else { freq = 0; } // square fraction to approach zero more quickly fraction *= fraction; // Sine wave that slowly settles to zero fraction = fraction * sin( gHUD.m_flTime * freq ); // add to view origin gHUD.m_Shake.appliedOffset = gHUD.m_Shake.offset * fraction; // add to roll gHUD.m_Shake.appliedAngle = gHUD.m_Shake.angle * fraction; // drop amplitude a bit, less for higher frequency shakes float localAmp = gHUD.m_Shake.amplitude * ( frametime / ( gHUD.m_Shake.duration * gHUD.m_Shake.frequency )); gHUD.m_Shake.amplitude -= localAmp; } //========================== // V_ApplyShake //========================== void V_ApplyShake( Vector& origin, Vector& angles, float factor ) { origin.MA( factor, origin, gHUD.m_Shake.appliedOffset ); angles.z += gHUD.m_Shake.appliedAngle * factor; } //========================== // V_CalcFinalPass //========================== void V_CalcFinalPass( ref_params_t *pparams ) { g_FirstFrame = true; // enable calc next passes V_ResetViewportRefdef( pparams ); // reset view port as default m_RenderRefCount++; // increase counter } //========================== // V_CalcThirdPersonRefdef //========================== void V_CalcThirdPersonRefdef( ref_params_t *pparams ) { // passed only in third person if( gHUD.m_iCameraMode == 0 || pparams->intermission ) return; // clear viewmodel for thirdperson edict_t *viewent = GetViewModel(); viewent->v.modelindex = 0; // get current values v_cl_angles = pparams->cl_viewangles; v_angles = pparams->viewangles; v_origin = pparams->vieworg; V_GetChasePos( pparams, GetLocalPlayer(), v_cl_angles, v_origin, v_angles ); // write back new values pparams->cl_viewangles = v_cl_angles; pparams->viewangles = v_angles; pparams->vieworg = v_origin; // apply shake for thirdperson too V_CalcShake(); V_ApplyShake( pparams->vieworg, pparams->viewangles, 1.0 ); } //========================== // V_CalcSendOrigin //========================== void V_CalcSendOrigin( ref_params_t *pparams ) { // never let view origin sit exactly on a node line, because a water plane can // dissapear when viewed with the eye exactly on it. pparams->vieworg[0] += 1.0 / 32; pparams->vieworg[1] += 1.0 / 32; pparams->vieworg[2] += 1.0 / 32; } //========================== // V_CalcWaterLevel //========================== float V_CalcWaterLevel( ref_params_t *pparams ) { float waterOffset = 0; if( pparams->waterlevel >= 2 ) { int i, contents, waterDist; waterDist = cl_waterdist->value; TraceResult tr; Vector point; TRACE_HULL( pparams->simorg, Vector(-16,-16,-24), Vector(16,16,32), pparams->simorg, 1, GetLocalPlayer(), &tr ); if( tr.pHit && !stricmp( STRING( tr.pHit->v.classname ), "func_water" )) waterDist += ( tr.pHit->v.scale * 16 ); point = pparams->vieworg; // 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 = POINT_CONTENTS( point ); 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 = POINT_CONTENTS( point ); if( contents <= CONTENTS_WATER ) break; point[2] -= 1; } waterOffset = (point[2] - waterDist) - pparams->vieworg[2]; } } // underwater refraction if( pparams->waterlevel == 3 ) { float f = sin( pparams->time * 0.4 * (M_PI * 2.7)); pparams->fov_x += f; pparams->fov_y -= f; } pparams->vieworg[2] += waterOffset; return waterOffset; } //========================== // V_CalcScrOffset //========================== void V_CalcScrOffset( ref_params_t *pparams ) { // don't allow cheats in multiplayer if( pparams->maxclients > 1 ) return; for( int i = 0; i < 3; i++ ) { pparams->vieworg[i] += scr_ofsx->value * pparams->forward[i]; pparams->vieworg[i] += scr_ofsy->value * pparams->right[i]; pparams->vieworg[i] += scr_ofsz->value * pparams->up[i]; } } //========================== // V_InterpolateOrigin //========================== void V_InterpolateOrigin( ref_params_t *pparams ) { edict_t *view; // view is the weapon model (only visible from inside body ) view = GetViewModel(); if( cl_vsmoothing->value && ( pparams->maxclients > 1 )) { int i, foundidx; float t; if( cl_vsmoothing->value < 0.0 ) CVAR_SET_FLOAT( "cl_vsmoothing", 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 Vector delta; double frac; double dt; Vector 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 ); delta = ViewInterp.Origins[(foundidx + 1) & ORIGIN_MASK] - ViewInterp.Origins[foundidx & ORIGIN_MASK]; neworg.MA( frac, ViewInterp.Origins[foundidx & ORIGIN_MASK], delta ); // don't interpolate large changes if( delta.Length() < 64 ) { delta = neworg - pparams->simorg; pparams->simorg += delta; pparams->vieworg += delta; view->v.origin += delta; } } } } } //========================== // V_CalcFirstPersonRefdef //========================== void V_CalcFirstPersonRefdef( ref_params_t *pparams ) { // don't pass in thirdperson or intermission if( gHUD.m_iCameraMode || pparams->intermission ) return; Vector angles; float bob, waterOffset; static float lasttime; edict_t *view = GetViewModel(); int i; if( g_bFinalPass ) V_DriftPitch( pparams ); bob = V_CalcBob( pparams ); // refresh the position if( !pparams->smoothing || pparams->demoplayback ) { // use interpolated values pparams->vieworg = pparams->simorg + pparams->viewheight; } // in-game use predicted values pparams->viewangles = pparams->cl_viewangles; pparams->vieworg[2] += ( bob ); V_CalcShake(); V_ApplyShake( pparams->vieworg, pparams->viewangles, 1.0 ); V_CalcSendOrigin( pparams ); waterOffset = V_CalcWaterLevel( pparams ); V_CalcViewRoll( pparams ); V_AddIdle( pparams ); // offsets angles = pparams->viewangles; AngleVectors( angles, pparams->forward, pparams->right, pparams->up ); V_CalcScrOffset( pparams ); view->v.angles = pparams->cl_viewangles; V_CalcGunAngle( pparams ); // use predicted origin as view origin. view->v.origin = pparams->vieworg; view->v.origin[2] += ( waterOffset ); // Let the viewmodel shake at about 10% of the amplitude V_ApplyShake( view->v.origin, view->v.angles, 0.9 ); for( i = 0; i < 3; i++ ) { view->v.origin[i] += bob * 0.2 * pparams->forward[i]; view->v.origin[i] += bob * 0.4 * pparams->right[i]; } view->v.origin[2] += (bob * 0.01f); view->v.angles[YAW] -= bob * 0.5; view->v.angles[ROLL] -= bob * 1; view->v.angles[PITCH] -= bob * 0.3; view->v.origin[2] -= 1; view->v.oldangles = view->v.angles; view->v.oldorigin = view->v.origin; if( g_bMirrorPass || g_bPortalPass || g_bScreenPass ) pparams->punchangle = -pparams->punchangle; // make inverse for mirror pparams->viewangles += pparams->punchangle; pparams->viewangles += ev_punchangle; V_DropPunchAngle( pparams->frametime, ev_punchangle ); static float oldz = 0; if( !pparams->smoothing && pparams->onground && pparams->simorg[2] - oldz > 0 ) { float steptime; steptime = pparams->time - lasttime; if( steptime < 0 ) steptime = 0; oldz += steptime * 150; if( oldz > pparams->vieworg[2]) oldz = pparams->vieworg[2]; if( pparams->vieworg[2] - oldz > pparams->movevars->stepheight ) oldz = pparams->vieworg[2] - pparams->movevars->stepheight; pparams->vieworg[2] += oldz - pparams->vieworg[2]; view->v.origin[2] += oldz - pparams->vieworg[2]; } else oldz = pparams->vieworg[2]; static Vector lastorg; Vector delta; delta = pparams->vieworg - lastorg; if( delta.Length() != 0.0 ) { ViewInterp.Origins[ViewInterp.CurrentOrigin & ORIGIN_MASK] = pparams->simorg; ViewInterp.OriginTime[ViewInterp.CurrentOrigin & ORIGIN_MASK] = pparams->time; ViewInterp.CurrentOrigin++; lastorg = pparams->vieworg; } V_InterpolateOrigin( pparams ); // smooth moving in multiplayer lasttime = pparams->time; v_origin = pparams->vieworg; } //========================== // V_CalcMainRefdef //========================== void V_CalcMainRefdef( ref_params_t *pparams ) { if( g_FirstFrame ) g_bFinalPass = true; V_CalcFirstPersonRefdef( pparams ); V_CalcThirdPersonRefdef( pparams ); V_CalcIntermisionRefdef( pparams ); V_CalcCameraRefdef( pparams ); g_vecBaseOrigin = pparams->vieworg; g_vecBaseAngles = pparams->viewangles; } //========================== // V_CalcSkyRefdef //========================== bool V_CalcSkyRefdef( ref_params_t *pparams ) { if( g_bSkyShouldpass ) { g_bSkyShouldpass = false; V_CalcMainRefdef( pparams ); // refresh position pparams->vieworg = gHUD.m_vecSkyPos; V_ResetViewportRefdef( pparams ); pparams->nextView = 1; g_bSkyPass = true; m_RenderRefCount++; return true; } if( g_bSkyPass ) { pparams->nextView = 0; g_bSkyPass = false; return false; } return false; } //========================== // V_CalcMirrorsRefdef //========================== bool V_CalcMirrorsRefdef( ref_params_t *pparams ) { if( g_bMirrorShouldpass ) { if( pparams->nextView == 0 ) { g_bMirrorPass = true; // this is first pass rendering (setup mirror viewport and nextView's) V_SetViewportRefdef( pparams ); // enable clip plane once in first mirror! g_engfuncs.pTriAPI->Enable( TRI_CLIP_PLANE ); V_CalcMainRefdef( pparams ); #ifdef XASH_RENDER m_pCurrentMirror = NULL; R_SetupNewMirror( pparams ); R_SetupMirrorRenderPass( pparams ); #endif pparams->nextView = g_iTotalVisibleMirrors; m_RenderRefCount++; return true; } else if( pparams->nextView == 1 ) { #ifdef XASH_RENDER R_NewMirrorRenderPass(); // capture view to texture m_pCurrentMirror = NULL; #endif g_engfuncs.pTriAPI->Disable( TRI_CLIP_PLANE ); V_ResetViewportRefdef( pparams ); pparams->nextView = 0; g_bMirrorPass = false; g_bMirrorShouldpass = false; return false; } else { #ifdef XASH_RENDER R_NewMirrorRenderPass(); // capture view to texture R_SetupNewMirror( pparams ); R_SetupMirrorRenderPass( pparams ); #endif pparams->nextView--; m_RenderRefCount++; return true; } } return false; } //========================== // V_CalcScreensRefdef //========================== bool V_CalcScreensRefdef( ref_params_t *pparams ) { if( g_bScreenShouldpass ) { if( pparams->nextView == 0 ) { // this is first pass rendering (setup screen viewport and nextView's) g_bScreenPass = true; V_SetViewportRefdef( pparams ); #ifdef XASH_RENDER m_pCurrentScreen = NULL; R_SetupNewScreen(pparams); R_SetupScreenRenderPass(pparams); #endif pparams->nextView = g_iTotalVisibleScreens; m_RenderRefCount++; return true;//end of pass } else if( pparams->nextView == 1 ) { #ifdef XASH_RENDER R_NewScreenRenderPass(); // capture view to texture m_pCurrentScreen = NULL; #endif V_ResetViewportRefdef( pparams ); pparams->nextView = 0; g_bScreenShouldpass = false; return false; } else { #ifdef XASH_RENDER R_NewScreenRenderPass(); // capture view to texture R_SetupNewScreen( pparams ); R_SetupScreenRenderPass( pparams ); #endif pparams->nextView--; m_RenderRefCount++; return true; } } return false; } //========================== // V_CalcPortalsRefdef //========================== bool V_CalcPortalsRefdef( ref_params_t *pparams ) { if( g_bPortalShouldpass ) { if( pparams->nextView == 0 ) { // this is first pass rendering (setup mirror viewport and nextView's) g_bPortalPass = true; V_SetViewportRefdef( pparams ); V_CalcMainRefdef( pparams ); #ifdef XASH_RENDER m_pCurrentPortal = NULL; R_SetupNewPortal( pparams ); R_SetupPortalRenderPass( pparams ); #endif pparams->nextView = g_iTotalVisiblePortals; m_RenderRefCount++; return true; } else if( pparams->nextView == 1 ) { #ifdef XASH_RENDER R_NewPortalRenderPass(); // capture view to texture m_pCurrentPortal = NULL; #endif // restore for final pass V_ResetViewportRefdef( pparams ); pparams->nextView = 0; g_bPortalShouldpass = false; return false; } else { #ifdef XASH_RENDER R_NewPortalRenderPass(); // capture view to texture R_SetupNewPortal( pparams ); R_SetupPortalRenderPass( pparams ); #endif pparams->nextView--; m_RenderRefCount++; return true; } } return false; } void V_CalcRefdef( ref_params_t *pparams ) { V_CalcNextView( pparams ); if( V_CalcSkyRefdef( pparams )) return; if( V_CalcScreensRefdef( pparams )) return; if( V_CalcPortalsRefdef( pparams )) return; if( V_CalcMirrorsRefdef( pparams )) return; V_CalcGlobalFog( pparams ); V_CalcFinalPass ( pparams ); V_CalcMainRefdef( pparams ); V_PrintDebugInfo( pparams ); }