codemp/cgame/cg_view.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // cg_view.c -- setup all the parameters (position, angle, etc)
00004 // for a 3D rendering
00005 #include "cg_local.h"
00006 
00007 #include "bg_saga.h"
00008 
00009 #if !defined(CL_LIGHT_H_INC)
00010         #include "cg_lights.h"
00011 #endif
00012 
00013 #define MASK_CAMERACLIP (MASK_SOLID|CONTENTS_PLAYERCLIP)
00014 #define CAMERA_SIZE     4
00015 
00016 
00017 /*
00018 =============================================================================
00019 
00020   MODEL TESTING
00021 
00022 The viewthing and gun positioning tools from Q2 have been integrated and
00023 enhanced into a single model testing facility.
00024 
00025 Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
00026 
00027 The names must be the full pathname after the basedir, like 
00028 "models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
00029 
00030 Testmodel will create a fake entity 100 units in front of the current view
00031 position, directly facing the viewer.  It will remain immobile, so you can
00032 move around it to view it from different angles.
00033 
00034 Testgun will cause the model to follow the player around and supress the real
00035 view weapon model.  The default frame 0 of most guns is completely off screen,
00036 so you will probably have to cycle a couple frames to see it.
00037 
00038 "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
00039 frame or skin of the testmodel.  These are bound to F5, F6, F7, and F8 in
00040 q3default.cfg.
00041 
00042 If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
00043 you adjust the positioning.
00044 
00045 Note that none of the model testing features update while the game is paused, so
00046 it may be convenient to test with deathmatch set to 1 so that bringing down the
00047 console doesn't pause the game.
00048 
00049 =============================================================================
00050 */
00051 
00052 /*
00053 =================
00054 CG_TestModel_f
00055 
00056 Creates an entity in front of the current position, which
00057 can then be moved around
00058 =================
00059 */
00060 void CG_TestModel_f (void) {
00061         vec3_t          angles;
00062 
00063         memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
00064         if ( trap_Argc() < 2 ) {
00065                 return;
00066         }
00067 
00068         Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
00069         cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
00070 
00071         if ( trap_Argc() == 3 ) {
00072                 cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
00073                 cg.testModelEntity.frame = 1;
00074                 cg.testModelEntity.oldframe = 0;
00075         }
00076         if (! cg.testModelEntity.hModel ) {
00077                 CG_Printf( "Can't register model\n" );
00078                 return;
00079         }
00080 
00081         VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
00082 
00083         angles[PITCH] = 0;
00084         angles[YAW] = 180 + cg.refdef.viewangles[1];
00085         angles[ROLL] = 0;
00086 
00087         AnglesToAxis( angles, cg.testModelEntity.axis );
00088         cg.testGun = qfalse;
00089 }
00090 
00091 /*
00092 =================
00093 CG_TestGun_f
00094 
00095 Replaces the current view weapon with the given model
00096 =================
00097 */
00098 void CG_TestGun_f (void) {
00099         CG_TestModel_f();
00100         cg.testGun = qtrue;
00101         //cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON;
00102 
00103         // rww - 9-13-01 [1-26-01-sof2]
00104         cg.testModelEntity.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON;
00105 }
00106 
00107 
00108 void CG_TestModelNextFrame_f (void) {
00109         cg.testModelEntity.frame++;
00110         CG_Printf( "frame %i\n", cg.testModelEntity.frame );
00111 }
00112 
00113 void CG_TestModelPrevFrame_f (void) {
00114         cg.testModelEntity.frame--;
00115         if ( cg.testModelEntity.frame < 0 ) {
00116                 cg.testModelEntity.frame = 0;
00117         }
00118         CG_Printf( "frame %i\n", cg.testModelEntity.frame );
00119 }
00120 
00121 void CG_TestModelNextSkin_f (void) {
00122         cg.testModelEntity.skinNum++;
00123         CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
00124 }
00125 
00126 void CG_TestModelPrevSkin_f (void) {
00127         cg.testModelEntity.skinNum--;
00128         if ( cg.testModelEntity.skinNum < 0 ) {
00129                 cg.testModelEntity.skinNum = 0;
00130         }
00131         CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
00132 }
00133 
00134 static void CG_AddTestModel (void) {
00135         int             i;
00136 
00137         // re-register the model, because the level may have changed
00138         cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
00139         if (! cg.testModelEntity.hModel ) {
00140                 CG_Printf ("Can't register model\n");
00141                 return;
00142         }
00143 
00144         // if testing a gun, set the origin reletive to the view origin
00145         if ( cg.testGun ) {
00146                 VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
00147                 VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] );
00148                 VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] );
00149                 VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] );
00150 
00151                 // allow the position to be adjusted
00152                 for (i=0 ; i<3 ; i++) {
00153                         cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value;
00154                         cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value;
00155                         cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value;
00156                 }
00157         }
00158 
00159         trap_R_AddRefEntityToScene( &cg.testModelEntity );
00160 }
00161 
00162 
00163 
00164 //============================================================================
00165 
00166 
00167 /*
00168 =================
00169 CG_CalcVrect
00170 
00171 Sets the coordinates of the rendered window
00172 =================
00173 */
00174 static void CG_CalcVrect (void) {
00175         int             size;
00176 
00177         // the intermission should allways be full screen
00178         if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
00179                 size = 100;
00180         } else {
00181                 // bound normal viewsize
00182                 if (cg_viewsize.integer < 30) {
00183                         trap_Cvar_Set ("cg_viewsize","30");
00184                         size = 30;
00185                 } else if (cg_viewsize.integer > 100) {
00186                         trap_Cvar_Set ("cg_viewsize","100");
00187                         size = 100;
00188                 } else {
00189                         size = cg_viewsize.integer;
00190                 }
00191 
00192         }
00193         cg.refdef.width = cgs.glconfig.vidWidth*size/100;
00194         cg.refdef.width &= ~1;
00195 
00196         cg.refdef.height = cgs.glconfig.vidHeight*size/100;
00197         cg.refdef.height &= ~1;
00198 
00199         cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2;
00200         cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2;
00201 }
00202 
00203 //==============================================================================
00204 
00205 //==============================================================================
00206 //==============================================================================
00207 // this causes a compiler bug on mac MrC compiler
00208 static void CG_StepOffset( void ) {
00209         int             timeDelta;
00210         
00211         // smooth out stair climbing
00212         timeDelta = cg.time - cg.stepTime;
00213         if ( timeDelta < STEP_TIME ) {
00214                 cg.refdef.vieworg[2] -= cg.stepChange 
00215                         * (STEP_TIME - timeDelta) / STEP_TIME;
00216         }
00217 }
00218 
00219 #define CAMERA_DAMP_INTERVAL    50
00220 
00221 static vec3_t   cameramins = { -CAMERA_SIZE, -CAMERA_SIZE, -CAMERA_SIZE };
00222 static vec3_t   cameramaxs = { CAMERA_SIZE, CAMERA_SIZE, CAMERA_SIZE };
00223 vec3_t  camerafwd, cameraup;
00224 
00225 vec3_t  cameraFocusAngles,                      cameraFocusLoc;
00226 vec3_t  cameraIdealTarget,                      cameraIdealLoc;
00227 vec3_t  cameraCurTarget={0,0,0},        cameraCurLoc={0,0,0};
00228 vec3_t  cameraOldLoc={0,0,0},           cameraNewLoc={0,0,0};
00229 int             cameraLastFrame=0;
00230 
00231 float   cameraLastYaw=0;
00232 float   cameraStiffFactor=0.0f;
00233 
00234 /*
00235 ===============
00236 Notes on the camera viewpoint in and out...
00237 
00238 cg.refdef.vieworg
00239 --at the start of the function holds the player actor's origin (center of player model).
00240 --it is set to the final view location of the camera at the end of the camera code.
00241 cg.refdef.viewangles
00242 --at the start holds the client's view angles
00243 --it is set to the final view angle of the camera at the end of the camera code.
00244 
00245 ===============
00246 */
00247   
00248 extern qboolean gCGHasFallVector;
00249 extern vec3_t gCGFallVector;
00250 
00251 /*
00252 ===============
00253 CG_CalcTargetThirdPersonViewLocation
00254 
00255 ===============
00256 */
00257 static void CG_CalcIdealThirdPersonViewTarget(void)
00258 {
00259         // Initialize IdealTarget
00260         if (gCGHasFallVector)
00261         {
00262                 VectorCopy(gCGFallVector, cameraFocusLoc);
00263         }
00264         else
00265         {
00266                 VectorCopy(cg.refdef.vieworg, cameraFocusLoc);
00267         }
00268 
00269         // Add in the new viewheight
00270         cameraFocusLoc[2] += cg.snap->ps.viewheight;
00271 
00272         // Add in a vertical offset from the viewpoint, which puts the actual target above the head, regardless of angle.
00273 //      VectorMA(cameraFocusLoc, thirdPersonVertOffset, cameraup, cameraIdealTarget);
00274         
00275         // Add in a vertical offset from the viewpoint, which puts the actual target above the head, regardless of angle.
00276         VectorCopy( cameraFocusLoc, cameraIdealTarget );
00277         
00278         {
00279                 float vertOffset = cg_thirdPersonVertOffset.value;
00280 
00281                 if (cg.snap && cg.snap->ps.m_iVehicleNum)
00282                 {
00283                         centity_t *veh = &cg_entities[cg.snap->ps.m_iVehicleNum];
00284                         if (veh->m_pVehicle &&
00285                                 veh->m_pVehicle->m_pVehicleInfo->cameraOverride)
00286                         { //override the range with what the vehicle wants it to be
00287                                 if ( veh->m_pVehicle->m_pVehicleInfo->cameraPitchDependantVertOffset )
00288                                 {
00289                                         if ( cg.snap->ps.viewangles[PITCH] > 0 )
00290                                         {
00291                                                 vertOffset = 130+cg.predictedPlayerState.viewangles[PITCH]*-10;
00292                                                 if ( vertOffset < -170 )
00293                                                 {
00294                                                         vertOffset = -170;
00295                                                 }
00296                                         }
00297                                         else if ( cg.snap->ps.viewangles[PITCH] < 0 )
00298                                         {
00299                                                 vertOffset = 130+cg.predictedPlayerState.viewangles[PITCH]*-5;
00300                                                 if ( vertOffset > 130 )
00301                                                 {
00302                                                         vertOffset = 130;
00303                                                 }
00304                                         }
00305                                         else
00306                                         {
00307                                                 vertOffset = 30;
00308                                         }
00309                                 }
00310                                 else 
00311                                 {
00312                                         vertOffset = veh->m_pVehicle->m_pVehicleInfo->cameraVertOffset;
00313                                 }
00314                         }
00315                         else if ( veh->m_pVehicle
00316                                 && veh->m_pVehicle->m_pVehicleInfo
00317                                 && veh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
00318                         {
00319                                 vertOffset = 0;
00320                         }
00321                 }
00322                 cameraIdealTarget[2] += vertOffset;
00323         }
00324         //VectorMA(cameraFocusLoc, cg_thirdPersonVertOffset.value, cameraup, cameraIdealTarget);
00325 }
00326 
00327         
00328 
00329 /*
00330 ===============
00331 CG_CalcTargetThirdPersonViewLocation
00332 
00333 ===============
00334 */
00335 static void CG_CalcIdealThirdPersonViewLocation(void)
00336 {
00337         float thirdPersonRange = cg_thirdPersonRange.value;
00338 
00339         if (cg.snap && cg.snap->ps.m_iVehicleNum)
00340         {
00341                 centity_t *veh = &cg_entities[cg.snap->ps.m_iVehicleNum];
00342                 if (veh->m_pVehicle &&
00343                         veh->m_pVehicle->m_pVehicleInfo->cameraOverride)
00344                 { //override the range with what the vehicle wants it to be
00345                         thirdPersonRange = veh->m_pVehicle->m_pVehicleInfo->cameraRange;
00346                         if ( veh->playerState->hackingTime )
00347                         {
00348                                 thirdPersonRange += fabs(((float)veh->playerState->hackingTime)/MAX_STRAFE_TIME) * 100.0f;
00349                         }
00350                 }
00351         }
00352 
00353         if ( cg.snap
00354                 && (cg.snap->ps.eFlags2&EF2_HELD_BY_MONSTER) 
00355                 && cg.snap->ps.hasLookTarget
00356                 && cg_entities[cg.snap->ps.lookTarget].currentState.NPC_class == CLASS_RANCOR )//only possibility for now, may add Wampa and sand creature later
00357         {//stay back
00358                 //thirdPersonRange = 180.0f;
00359                 thirdPersonRange = 120.0f;
00360         }
00361 
00362         VectorMA(cameraIdealTarget, -(thirdPersonRange), camerafwd, cameraIdealLoc);
00363 }
00364 
00365 
00366 
00367 static void CG_ResetThirdPersonViewDamp(void)
00368 {
00369         trace_t trace;
00370 
00371         // Cap the pitch within reasonable limits
00372         if (cameraFocusAngles[PITCH] > 89.0)
00373         {
00374                 cameraFocusAngles[PITCH] = 89.0;
00375         }
00376         else if (cameraFocusAngles[PITCH] < -89.0)
00377         {
00378                 cameraFocusAngles[PITCH] = -89.0;
00379         }
00380 
00381         AngleVectors(cameraFocusAngles, camerafwd, NULL, cameraup);
00382 
00383         // Set the cameraIdealTarget
00384         CG_CalcIdealThirdPersonViewTarget();
00385 
00386         // Set the cameraIdealLoc
00387         CG_CalcIdealThirdPersonViewLocation();
00388 
00389         // Now, we just set everything to the new positions.
00390         VectorCopy(cameraIdealLoc, cameraCurLoc);
00391         VectorCopy(cameraIdealTarget, cameraCurTarget);
00392 
00393         // First thing we do is trace from the first person viewpoint out to the new target location.
00394         CG_Trace(&trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurTarget, cg.snap->ps.clientNum, MASK_CAMERACLIP);
00395         if (trace.fraction <= 1.0)
00396         {
00397                 VectorCopy(trace.endpos, cameraCurTarget);
00398         }
00399 
00400         // Now we trace from the new target location to the new view location, to make sure there is nothing in the way.
00401         CG_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, cg.snap->ps.clientNum, MASK_CAMERACLIP);
00402         if (trace.fraction <= 1.0)
00403         {
00404                 VectorCopy(trace.endpos, cameraCurLoc);
00405         }
00406 
00407         cameraLastFrame = cg.time;
00408         cameraLastYaw = cameraFocusAngles[YAW];
00409         cameraStiffFactor = 0.0f;
00410 }
00411 
00412 // This is called every frame.
00413 static void CG_UpdateThirdPersonTargetDamp(void)
00414 {
00415         trace_t trace;
00416         vec3_t  targetdiff;
00417         float   dampfactor, dtime, ratio;
00418 
00419         // Set the cameraIdealTarget
00420         // Automatically get the ideal target, to avoid jittering.
00421         CG_CalcIdealThirdPersonViewTarget();
00422 
00423         if ( cg.predictedVehicleState.hyperSpaceTime
00424                 && (cg.time-cg.predictedVehicleState.hyperSpaceTime) < HYPERSPACE_TIME )
00425         {//hyperspacing, no damp
00426                 VectorCopy(cameraIdealTarget, cameraCurTarget);
00427         }
00428         else if (cg_thirdPersonTargetDamp.value>=1.0||cg.thisFrameTeleport||cg.predictedPlayerState.m_iVehicleNum)
00429         {       // No damping.
00430                 VectorCopy(cameraIdealTarget, cameraCurTarget);
00431         }
00432         else if (cg_thirdPersonTargetDamp.value>=0.0)
00433         {       
00434                 // Calculate the difference from the current position to the new one.
00435                 VectorSubtract(cameraIdealTarget, cameraCurTarget, targetdiff);
00436 
00437                 // Now we calculate how much of the difference we cover in the time allotted.
00438                 // The equation is (Damp)^(time)
00439                 dampfactor = 1.0-cg_thirdPersonTargetDamp.value;        // We must exponent the amount LEFT rather than the amount bled off
00440                 dtime = (float)(cg.time-cameraLastFrame) * (1.0/(float)CAMERA_DAMP_INTERVAL);   // Our dampfactor is geared towards a time interval equal to "1".
00441 
00442                 // Note that since there are a finite number of "practical" delta millisecond values possible, 
00443                 // the ratio should be initialized into a chart ultimately.
00444                 ratio = powf(dampfactor, dtime);
00445                 
00446                 // This value is how much distance is "left" from the ideal.
00447                 VectorMA(cameraIdealTarget, -ratio, targetdiff, cameraCurTarget);
00449         }
00450 
00451         // Now we trace to see if the new location is cool or not.
00452 
00453         // First thing we do is trace from the first person viewpoint out to the new target location.
00454         CG_Trace(&trace, cameraFocusLoc, cameramins, cameramaxs, cameraCurTarget, cg.snap->ps.clientNum, MASK_CAMERACLIP);
00455         if (trace.fraction < 1.0)
00456         {
00457                 VectorCopy(trace.endpos, cameraCurTarget);
00458         }
00459 
00460         // Note that previously there was an upper limit to the number of physics traces that are done through the world
00461         // for the sake of camera collision, since it wasn't calced per frame.  Now it is calculated every frame.
00462         // This has the benefit that the camera is a lot smoother now (before it lerped between tested points),
00463         // however two full volume traces each frame is a bit scary to think about.
00464 }
00465 
00466 // This can be called every interval, at the user's discretion.
00467 extern void CG_CalcEntityLerpPositions( centity_t *cent ); //cg_ents.c
00468 static void CG_UpdateThirdPersonCameraDamp(void)
00469 {
00470         trace_t trace;
00471         vec3_t  locdiff;
00472         float dampfactor, dtime, ratio;
00473 
00474         // Set the cameraIdealLoc
00475         CG_CalcIdealThirdPersonViewLocation();
00476         
00477         
00478         // First thing we do is calculate the appropriate damping factor for the camera.
00479         dampfactor=0.0;
00480         if ( cg.predictedVehicleState.hyperSpaceTime
00481                 && (cg.time-cg.predictedVehicleState.hyperSpaceTime) < HYPERSPACE_TIME )
00482         {//hyperspacing - don't damp camera
00483                 dampfactor = 1.0f;
00484         }
00485         else if (cg_thirdPersonCameraDamp.value != 0.0)
00486         {
00487                 float pitch;
00488                 float dFactor;
00489 
00490                 if (!cg.predictedPlayerState.m_iVehicleNum)
00491                 {
00492                         dFactor = cg_thirdPersonCameraDamp.value;
00493                 }
00494                 else
00495                 {
00496                         dFactor = 1.0f;
00497                 }
00498 
00499                 // Note that the camera pitch has already been capped off to 89.
00500                 pitch = Q_fabs(cameraFocusAngles[PITCH]);
00501 
00502                 // The higher the pitch, the larger the factor, so as you look up, it damps a lot less.
00503                 pitch /= 115.0; 
00504                 dampfactor = (1.0-dFactor)*(pitch*pitch);
00505 
00506                 dampfactor += dFactor;
00507 
00508                 // Now we also multiply in the stiff factor, so that faster yaw changes are stiffer.
00509                 if (cameraStiffFactor > 0.0f)
00510                 {       // The cameraStiffFactor is how much of the remaining damp below 1 should be shaved off, i.e. approach 1 as stiffening increases.
00511                         dampfactor += (1.0-dampfactor)*cameraStiffFactor;
00512                 }
00513         }
00514 
00515         if (dampfactor>=1.0||cg.thisFrameTeleport)
00516         {       // No damping.
00517                 VectorCopy(cameraIdealLoc, cameraCurLoc);
00518         }
00519         else if (dampfactor>=0.0)
00520         {       
00521                 // Calculate the difference from the current position to the new one.
00522                 VectorSubtract(cameraIdealLoc, cameraCurLoc, locdiff);
00523 
00524                 // Now we calculate how much of the difference we cover in the time allotted.
00525                 // The equation is (Damp)^(time)
00526                 dampfactor = 1.0-dampfactor;    // We must exponent the amount LEFT rather than the amount bled off
00527                 dtime = (float)(cg.time-cameraLastFrame) * (1.0/(float)CAMERA_DAMP_INTERVAL);   // Our dampfactor is geared towards a time interval equal to "1".
00528 
00529                 // Note that since there are a finite number of "practical" delta millisecond values possible, 
00530                 // the ratio should be initialized into a chart ultimately.
00531                 ratio = powf(dampfactor, dtime);
00532                 
00533                 // This value is how much distance is "left" from the ideal.
00534                 VectorMA(cameraIdealLoc, -ratio, locdiff, cameraCurLoc);
00536         }
00537 
00538         // Now we trace from the new target location to the new view location, to make sure there is nothing in the way.
00539         CG_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, cg.snap->ps.clientNum, MASK_CAMERACLIP);
00540 
00541         if (trace.fraction < 1.0)
00542         {
00543                 if (trace.entityNum < ENTITYNUM_WORLD &&
00544                         cg_entities[trace.entityNum].currentState.solid == SOLID_BMODEL &&
00545                         cg_entities[trace.entityNum].currentState.eType == ET_MOVER)
00546                 { //get a different position for movers -rww
00547                         centity_t *mover = &cg_entities[trace.entityNum];
00548 
00549                         //this is absolutely hackiful, since we calc view values before we add packet ents and lerp,
00550                         //if we hit a mover we want to update its lerp pos and force it when we do the trace against
00551                         //it.
00552                         if (mover->currentState.pos.trType != TR_STATIONARY &&
00553                                 mover->currentState.pos.trType != TR_LINEAR)
00554                         {
00555                                 int curTr = mover->currentState.pos.trType;
00556                                 vec3_t curTrB;
00557 
00558                                 VectorCopy(mover->currentState.pos.trBase, curTrB);
00559 
00560                                 //calc lerporigin for this client frame
00561                                 CG_CalcEntityLerpPositions(mover);
00562 
00563                                 //force the calc'd lerp to be the base and say we are stationary so we don't try to extrapolate
00564                                 //out further.
00565                                 mover->currentState.pos.trType = TR_STATIONARY;
00566                                 VectorCopy(mover->lerpOrigin, mover->currentState.pos.trBase);
00567                                 
00568                                 //retrace
00569                                 CG_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, cg.snap->ps.clientNum, MASK_CAMERACLIP);
00570 
00571                                 //copy old data back in
00572                                 mover->currentState.pos.trType = (trType_t) curTr;
00573                                 VectorCopy(curTrB, mover->currentState.pos.trBase);
00574                         }
00575                         if (trace.fraction < 1.0f)
00576                         { //still hit it, so take the proper trace endpos and use that.
00577                                 VectorCopy(trace.endpos, cameraCurLoc);
00578                         }
00579                 }
00580                 else
00581                 {
00582                         VectorCopy( trace.endpos, cameraCurLoc );
00583                 }
00584         }
00585 
00586         // Note that previously there was an upper limit to the number of physics traces that are done through the world
00587         // for the sake of camera collision, since it wasn't calced per frame.  Now it is calculated every frame.
00588         // This has the benefit that the camera is a lot smoother now (before it lerped between tested points),
00589         // however two full volume traces each frame is a bit scary to think about.
00590 }
00591 
00592 
00593 
00594 
00595 /*
00596 ===============`
00597 CG_OffsetThirdPersonView
00598 
00599 ===============
00600 */
00601 extern vmCvar_t cg_thirdPersonHorzOffset;
00602 extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
00603 static void CG_OffsetThirdPersonView( void ) 
00604 {
00605         vec3_t diff;
00606         float thirdPersonHorzOffset = cg_thirdPersonHorzOffset.value;
00607         float deltayaw;
00608 
00609         if (cg.snap && cg.snap->ps.m_iVehicleNum)
00610         {
00611                 centity_t *veh = &cg_entities[cg.snap->ps.m_iVehicleNum];
00612                 if (veh->m_pVehicle &&
00613                         veh->m_pVehicle->m_pVehicleInfo->cameraOverride)
00614                 { //override the range with what the vehicle wants it to be
00615                         thirdPersonHorzOffset = veh->m_pVehicle->m_pVehicleInfo->cameraHorzOffset;
00616                         if ( veh->playerState->hackingTime )
00617                         {
00618                                 thirdPersonHorzOffset += (((float)veh->playerState->hackingTime)/MAX_STRAFE_TIME) * -80.0f;
00619                         }
00620                 }
00621         }
00622 
00623         cameraStiffFactor = 0.0;
00624 
00625         // Set camera viewing direction.
00626         VectorCopy( cg.refdef.viewangles, cameraFocusAngles );
00627 
00628         // if dead, look at killer
00629         if ( cg.snap
00630                 && (cg.snap->ps.eFlags2&EF2_HELD_BY_MONSTER) 
00631                 && cg.snap->ps.hasLookTarget
00632                 && cg_entities[cg.snap->ps.lookTarget].currentState.NPC_class == CLASS_RANCOR )//only possibility for now, may add Wampa and sand creature later
00633         {//being held
00634                 //vec3_t monsterPos, dir2Me;
00635                 centity_t       *monster = &cg_entities[cg.snap->ps.lookTarget];
00636                 VectorSet( cameraFocusAngles, 0, AngleNormalize180(monster->lerpAngles[YAW]+180), 0 );
00637                 //make the look angle the vector from his mouth to me
00638                 /*
00639                 VectorCopy( monster->lerpOrigin, monsterPos );
00640                 monsterPos[2] = cg.snap->ps.origin[2];
00641                 VectorSubtract( monsterPos, cg.snap->ps.origin, dir2Me );
00642                 vectoangles( dir2Me, cameraFocusAngles );
00643                 */
00644         }
00645         else if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) 
00646         {
00647                 cameraFocusAngles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
00648         }
00649         else
00650         {       // Add in the third Person Angle.
00651                 cameraFocusAngles[YAW] += cg_thirdPersonAngle.value;
00652                 {
00653                         float pitchOffset = cg_thirdPersonPitchOffset.value;
00654                         if (cg.snap && cg.snap->ps.m_iVehicleNum)
00655                         {
00656                                 centity_t *veh = &cg_entities[cg.snap->ps.m_iVehicleNum];
00657                                 if (veh->m_pVehicle &&
00658                                         veh->m_pVehicle->m_pVehicleInfo->cameraOverride)
00659                                 { //override the range with what the vehicle wants it to be
00660                                         if ( veh->m_pVehicle->m_pVehicleInfo->cameraPitchDependantVertOffset )
00661                                         {
00662                                                 if ( cg.snap->ps.viewangles[PITCH] > 0 )
00663                                                 {
00664                                                         pitchOffset = cg.predictedPlayerState.viewangles[PITCH]*-0.75;
00665                                                 }
00666                                                 else if ( cg.snap->ps.viewangles[PITCH] < 0 )
00667                                                 {
00668                                                         pitchOffset = cg.predictedPlayerState.viewangles[PITCH]*-0.75;
00669                                                 }
00670                                                 else
00671                                                 {
00672                                                         pitchOffset = 0;
00673                                                 }
00674                                         }
00675                                         else
00676                                         {
00677                                                 pitchOffset = veh->m_pVehicle->m_pVehicleInfo->cameraPitchOffset;
00678                                         }
00679                                 }
00680                         }
00681                         if ( 0 && cg.predictedPlayerState.m_iVehicleNum //in a vehicle
00682                                 && BG_UnrestrainedPitchRoll( &cg.predictedPlayerState, cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle ) )//can roll/pitch without restriction
00683                         {
00684                                 float pitchPerc = ((90.0f-fabs(cameraFocusAngles[ROLL]))/90.0f);
00685                                 cameraFocusAngles[PITCH] += pitchOffset*pitchPerc;
00686                                 if ( cameraFocusAngles[ROLL] > 0 )
00687                                 {
00688                                         cameraFocusAngles[YAW] -= pitchOffset-(pitchOffset*pitchPerc);
00689                                 }
00690                                 else
00691                                 {
00692                                         cameraFocusAngles[YAW] += pitchOffset-(pitchOffset*pitchPerc);
00693                                 }
00694                         }
00695                         else
00696                         {
00697                                 cameraFocusAngles[PITCH] += pitchOffset;
00698                         }
00699                 }
00700         }
00701 
00702         // The next thing to do is to see if we need to calculate a new camera target location.
00703 
00704         // If we went back in time for some reason, or if we just started, reset the sample.
00705         if (cameraLastFrame == 0 || cameraLastFrame > cg.time)
00706         {
00707                 CG_ResetThirdPersonViewDamp();
00708         }
00709         else
00710         {
00711                 // Cap the pitch within reasonable limits
00712                 if ( cg.predictedPlayerState.m_iVehicleNum //in a vehicle
00713                         && BG_UnrestrainedPitchRoll( &cg.predictedPlayerState, cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle ) )//can roll/pitch without restriction
00714                 {//no clamp on pitch
00715                         //FIXME: when pitch >= 90 or <= -90, camera rotates oddly... need to CrossProduct not just vectoangles
00716                 }
00717                 else
00718                 {
00719                         if (cameraFocusAngles[PITCH] > 80.0)
00720                         {
00721                                 cameraFocusAngles[PITCH] = 80.0;
00722                         }
00723                         else if (cameraFocusAngles[PITCH] < -80.0)
00724                         {
00725                                 cameraFocusAngles[PITCH] = -80.0;
00726                         }
00727                 }
00728 
00729                 AngleVectors(cameraFocusAngles, camerafwd, NULL, cameraup);
00730 
00731                 deltayaw = fabs(cameraFocusAngles[YAW] - cameraLastYaw);
00732                 if (deltayaw > 180.0f)
00733                 { // Normalize this angle so that it is between 0 and 180.
00734                         deltayaw = fabs(deltayaw - 360.0f);
00735                 }
00736                 cameraStiffFactor = deltayaw / (float)(cg.time-cameraLastFrame);
00737                 if (cameraStiffFactor < 1.0)
00738                 {
00739                         cameraStiffFactor = 0.0;
00740                 }
00741                 else if (cameraStiffFactor > 2.5)
00742                 {
00743                         cameraStiffFactor = 0.75;
00744                 }
00745                 else
00746                 {       // 1 to 2 scales from 0.0 to 0.5
00747                         cameraStiffFactor = (cameraStiffFactor-1.0f)*0.5f;
00748                 }
00749                 cameraLastYaw = cameraFocusAngles[YAW];
00750 
00751                 // Move the target to the new location.
00752                 CG_UpdateThirdPersonTargetDamp();
00753                 CG_UpdateThirdPersonCameraDamp();
00754         }
00755 
00756         // Now interestingly, the Quake method is to calculate a target focus point above the player, and point the camera at it.
00757         // We won't do that for now.
00758 
00759         // We must now take the angle taken from the camera target and location.
00760         /*VectorSubtract(cameraCurTarget, cameraCurLoc, diff);
00761         VectorNormalize(diff);
00762         vectoangles(diff, cg.refdef.viewangles);*/
00763         VectorSubtract(cameraCurTarget, cameraCurLoc, diff);
00764         {
00765                 float dist = VectorNormalize(diff);
00766                 //under normal circumstances, should never be 0.00000 and so on.
00767                 if ( !dist || (diff[0] == 0 || diff[1] == 0) )
00768                 {//must be hitting something, need some value to calc angles, so use cam forward
00769                         VectorCopy( camerafwd, diff );
00770                 }
00771         }
00772         if ( 0 && cg.predictedPlayerState.m_iVehicleNum //in a vehicle
00773                 && BG_UnrestrainedPitchRoll( &cg.predictedPlayerState, cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle ) )//can roll/pitch without restriction
00774         {//FIXME: this causes camera jerkiness, need to blend the roll?
00775                 float sav_Roll = cg.refdef.viewangles[ROLL];
00776                 vectoangles(diff, cg.refdef.viewangles);
00777                 cg.refdef.viewangles[ROLL] = sav_Roll;
00778         }
00779         else
00780         {
00781                 vectoangles(diff, cg.refdef.viewangles);
00782         }
00783 
00784         // Temp: just move the camera to the side a bit
00785         if ( thirdPersonHorzOffset != 0.0f )
00786         {
00787                 AnglesToAxis( cg.refdef.viewangles, cg.refdef.viewaxis );
00788                 VectorMA( cameraCurLoc, thirdPersonHorzOffset, cg.refdef.viewaxis[1], cameraCurLoc );
00789         }
00790 
00791         // ...and of course we should copy the new view location to the proper spot too.
00792         VectorCopy(cameraCurLoc, cg.refdef.vieworg);
00793 
00794         cameraLastFrame=cg.time;
00795 }
00796 
00797 void CG_GetVehicleCamPos( vec3_t camPos )
00798 {
00799         VectorCopy( cg.refdef.vieworg, camPos );
00800 }
00801 
00802 /*
00803 ===============
00804 CG_OffsetThirdPersonView
00805 
00806 ===============
00807 *//*
00808 #define FOCUS_DISTANCE  512
00809 static void CG_OffsetThirdPersonView( void ) {
00810         vec3_t          forward, right, up;
00811         vec3_t          view;
00812         vec3_t          focusAngles;
00813         trace_t         trace;
00814         static vec3_t   mins = { -4, -4, -4 };
00815         static vec3_t   maxs = { 4, 4, 4 };
00816         vec3_t          focusPoint;
00817         float           focusDist;
00818         float           forwardScale, sideScale;
00819 
00820         cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;
00821 
00822         VectorCopy( cg.refdef.viewangles, focusAngles );
00823 
00824         // if dead, look at killer
00825         if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
00826                 focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
00827                 cg.refdef.viewangles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
00828         }
00829 
00830         if ( focusAngles[PITCH] > 45 ) {
00831                 focusAngles[PITCH] = 45;                // don't go too far overhead
00832         }
00833         AngleVectors( focusAngles, forward, NULL, NULL );
00834 
00835         VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
00836 
00837         VectorCopy( cg.refdef.vieworg, view );
00838 
00839         view[2] += 8;
00840 
00841         cg.refdef.viewangles[PITCH] *= 0.5;
00842 
00843         AngleVectors( cg.refdef.viewangles, forward, right, up );
00844 
00845         forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
00846         sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
00847         VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
00848         VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
00849 
00850         // trace a ray from the origin to the viewpoint to make sure the view isn't
00851         // in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything
00852 
00853         if (!cg_cameraMode.integer) {
00854                 CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_CAMERACLIP);
00855 
00856                 if ( trace.fraction != 1.0 ) {
00857                         VectorCopy( trace.endpos, view );
00858                         view[2] += (1.0 - trace.fraction) * 32;
00859                         // try another trace to this position, because a tunnel may have the ceiling
00860                         // close enogh that this is poking out
00861 
00862                         CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_CAMERACLIP);
00863                         VectorCopy( trace.endpos, view );
00864                 }
00865         }
00866 
00867 
00868         VectorCopy( view, cg.refdef.vieworg );
00869 
00870         // select pitch to look at focus point from vieword
00871         VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
00872         focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
00873         if ( focusDist < 1 ) {
00874                 focusDist = 1;  // should never happen
00875         }
00876         cg.refdef.viewangles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
00877         cg.refdef.viewangles[YAW] -= cg_thirdPersonAngle.value;
00878 }
00879 
00880 
00881 // this causes a compiler bug on mac MrC compiler
00882 static void CG_StepOffset( void ) {
00883         int             timeDelta;
00884         
00885         // smooth out stair climbing
00886         timeDelta = cg.time - cg.stepTime;
00887         if ( timeDelta < STEP_TIME ) {
00888                 cg.refdef.vieworg[2] -= cg.stepChange 
00889                         * (STEP_TIME - timeDelta) / STEP_TIME;
00890         }
00891 }*/
00892 
00893 /*
00894 ===============
00895 CG_OffsetFirstPersonView
00896 
00897 ===============
00898 */
00899 static void CG_OffsetFirstPersonView( void ) {
00900         float                   *origin;
00901         float                   *angles;
00902         float                   bob;
00903         float                   ratio;
00904         float                   delta;
00905         float                   speed;
00906         float                   f;
00907         vec3_t                  predictedVelocity;
00908         int                             timeDelta;
00909         int                             kickTime;
00910         
00911         if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
00912                 return;
00913         }
00914 
00915         origin = cg.refdef.vieworg;
00916         angles = cg.refdef.viewangles;
00917 
00918         // if dead, fix the angle and don't add any kick
00919         if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
00920                 angles[ROLL] = 40;
00921                 angles[PITCH] = -15;
00922                 angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
00923                 origin[2] += cg.predictedPlayerState.viewheight;
00924                 return;
00925         }
00926 
00927         // add angles based on weapon kick
00928         kickTime = (cg.time - cg.kick_time);
00929         if ( kickTime < 800 )
00930         {//kicks are always 1 second long.  Deal with it.
00931                 float kickPerc = 0.0f;
00932                 if ( kickTime <= 200 )
00933                 {//winding up
00934                         kickPerc = kickTime/200.0f;
00935                 }
00936                 else
00937                 {//returning to normal
00938                         kickTime = 800 - kickTime;
00939                         kickPerc = kickTime/600.0f;
00940                 }
00941                 VectorMA( angles, kickPerc, cg.kick_angles, angles );
00942         }
00943         // add angles based on damage kick
00944         if ( cg.damageTime ) {
00945                 ratio = cg.time - cg.damageTime;
00946                 if ( ratio < DAMAGE_DEFLECT_TIME ) {
00947                         ratio /= DAMAGE_DEFLECT_TIME;
00948                         angles[PITCH] += ratio * cg.v_dmg_pitch;
00949                         angles[ROLL] += ratio * cg.v_dmg_roll;
00950                 } else {
00951                         ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
00952                         if ( ratio > 0 ) {
00953                                 angles[PITCH] += ratio * cg.v_dmg_pitch;
00954                                 angles[ROLL] += ratio * cg.v_dmg_roll;
00955                         }
00956                 }
00957         }
00958 
00959         // add pitch based on fall kick
00960 #if 0
00961         ratio = ( cg.time - cg.landTime) / FALL_TIME;
00962         if (ratio < 0)
00963                 ratio = 0;
00964         angles[PITCH] += ratio * cg.fall_value;
00965 #endif
00966 
00967         // add angles based on velocity
00968         VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );
00969 
00970         delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
00971         angles[PITCH] += delta * cg_runpitch.value;
00972         
00973         delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
00974         angles[ROLL] -= delta * cg_runroll.value;
00975 
00976         // add angles based on bob
00977 
00978         // make sure the bob is visible even at low speeds
00979         speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
00980 
00981         delta = cg.bobfracsin * cg_bobpitch.value * speed;
00982         if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
00983                 delta *= 3;             // crouching
00984         angles[PITCH] += delta;
00985         delta = cg.bobfracsin * cg_bobroll.value * speed;
00986         if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
00987                 delta *= 3;             // crouching accentuates roll
00988         if (cg.bobcycle & 1)
00989                 delta = -delta;
00990         angles[ROLL] += delta;
00991 
00992 //===================================
00993 
00994         // add view height
00995         origin[2] += cg.predictedPlayerState.viewheight;
00996 
00997         // smooth out duck height changes
00998         timeDelta = cg.time - cg.duckTime;
00999         if ( timeDelta < DUCK_TIME) {
01000                 cg.refdef.vieworg[2] -= cg.duckChange 
01001                         * (DUCK_TIME - timeDelta) / DUCK_TIME;
01002         }
01003 
01004         // add bob height
01005         bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
01006         if (bob > 6) {
01007                 bob = 6;
01008         }
01009 
01010         origin[2] += bob;
01011 
01012 
01013         // add fall height
01014         delta = cg.time - cg.landTime;
01015         if ( delta < LAND_DEFLECT_TIME ) {
01016                 f = delta / LAND_DEFLECT_TIME;
01017                 cg.refdef.vieworg[2] += cg.landChange * f;
01018         } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
01019                 delta -= LAND_DEFLECT_TIME;
01020                 f = 1.0 - ( delta / LAND_RETURN_TIME );
01021                 cg.refdef.vieworg[2] += cg.landChange * f;
01022         }
01023 
01024         // add step offset
01025         CG_StepOffset();
01026 
01027         // add kick offset
01028 
01029         VectorAdd (origin, cg.kick_origin, origin);
01030 
01031         // pivot the eye based on a neck length
01032 #if 0
01033         {
01034 #define NECK_LENGTH             8
01035         vec3_t                  forward, up;
01036  
01037         cg.refdef.vieworg[2] -= NECK_LENGTH;
01038         AngleVectors( cg.refdef.viewangles, forward, NULL, up );
01039         VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
01040         VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
01041         }
01042 #endif
01043 }
01044 
01045 static void CG_OffsetFighterView( void )
01046 {
01047         vec3_t vehFwd, vehRight, vehUp, backDir;
01048         vec3_t  camOrg, camBackOrg;
01049         float horzOffset = cg_thirdPersonHorzOffset.value;
01050         float vertOffset = cg_thirdPersonVertOffset.value;
01051         float pitchOffset = cg_thirdPersonPitchOffset.value;
01052         float yawOffset = cg_thirdPersonAngle.value;
01053         float range = cg_thirdPersonRange.value;
01054         trace_t trace;
01055         centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
01056 
01057         AngleVectors( cg.refdef.viewangles, vehFwd, vehRight, vehUp );
01058 
01059         if ( veh->m_pVehicle &&
01060                 veh->m_pVehicle->m_pVehicleInfo->cameraOverride )
01061         { //override the horizontal offset with what the vehicle wants it to be
01062                 horzOffset = veh->m_pVehicle->m_pVehicleInfo->cameraHorzOffset;
01063                 vertOffset = veh->m_pVehicle->m_pVehicleInfo->cameraVertOffset;
01064                 //NOTE: no yaw offset?
01065                 pitchOffset = veh->m_pVehicle->m_pVehicleInfo->cameraPitchOffset;
01066                 range = veh->m_pVehicle->m_pVehicleInfo->cameraRange;
01067                 if ( veh->playerState->hackingTime )
01068                 {
01069                         horzOffset += (((float)veh->playerState->hackingTime)/MAX_STRAFE_TIME) * -80.0f;
01070                         range += fabs(((float)veh->playerState->hackingTime)/MAX_STRAFE_TIME) * 100.0f;
01071                 }
01072         }
01073 
01074         //Set camera viewing position
01075         VectorMA( cg.refdef.vieworg, horzOffset, vehRight, camOrg );
01076         VectorMA( camOrg, vertOffset, vehUp, camOrg );
01077 
01078         //trace to that pos
01079         CG_Trace(&trace, cg.refdef.vieworg, cameramins, cameramaxs, camOrg, cg.snap->ps.clientNum, MASK_CAMERACLIP);
01080         if ( trace.fraction < 1.0 )
01081         {
01082                 VectorCopy( trace.endpos, camOrg );
01083         }
01084 
01085         // Set camera viewing direction.
01086         cg.refdef.viewangles[YAW] += yawOffset;
01087         cg.refdef.viewangles[PITCH] += pitchOffset;
01088 
01089         //Now bring the cam back from that pos and angles at range
01090         AngleVectors( cg.refdef.viewangles, backDir, NULL, NULL );
01091         VectorScale( backDir, -1, backDir );
01092 
01093         VectorMA( camOrg, range, backDir, camBackOrg );
01094 
01095         //trace to that pos
01096         CG_Trace(&trace, camOrg, cameramins, cameramaxs, camBackOrg, cg.snap->ps.clientNum, MASK_CAMERACLIP);
01097         VectorCopy( trace.endpos, camOrg );
01098 
01099         //FIXME: do we need to smooth the org?
01100         // ...and of course we should copy the new view location to the proper spot too.
01101         VectorCopy(camOrg, cg.refdef.vieworg);
01102 }
01103 //======================================================================
01104 
01105 void CG_ZoomDown_f( void ) { 
01106         if ( cg.zoomed ) {
01107                 return;
01108         }
01109         cg.zoomed = qtrue;
01110         cg.zoomTime = cg.time;
01111 }
01112 
01113 void CG_ZoomUp_f( void ) { 
01114         if ( !cg.zoomed ) {
01115                 return;
01116         }
01117         cg.zoomed = qfalse;
01118         cg.zoomTime = cg.time;
01119 }
01120 
01121 
01122 
01123 /*
01124 ====================
01125 CG_CalcFovFromX
01126 
01127 Calcs Y FOV from given X FOV
01128 ====================
01129 */
01130 qboolean CG_CalcFOVFromX( float fov_x ) 
01131 {
01132         float   x;
01133 //      float   phase;
01134 //      float   v;
01135 //      int             contents;
01136         float   fov_y;
01137         qboolean        inwater;
01138 
01139         x = cg.refdef.width / tan( fov_x / 360 * M_PI );
01140         fov_y = atan2( cg.refdef.height, x );
01141         fov_y = fov_y * 360 / M_PI;
01142 
01143         // there's a problem with this, it only takes the leafbrushes into account, not the entity brushes,
01144         //      so if you give slime/water etc properties to a func_door area brush in order to move the whole water 
01145         //      level up/down this doesn't take into account the door position, so warps the view the whole time
01146         //      whether the water is up or not. Fortunately there's only one slime area in Trek that you can be under,
01147         //      so lose it...
01148 #if 0
01149 /*
01150         // warp if underwater
01151         contents = CG_PointContents( cg.refdef.vieworg, -1 );
01152         if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
01153                 phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
01154                 v = WAVE_AMPLITUDE * sin( phase );
01155                 fov_x += v;
01156                 fov_y -= v;
01157                 inwater = qtrue;
01158         }
01159         else {
01160                 inwater = qfalse;
01161         }
01162 */
01163 #else
01164         inwater = qfalse;
01165 #endif
01166 
01167 
01168         // set it
01169         cg.refdef.fov_x = fov_x;
01170         cg.refdef.fov_y = fov_y;
01171 
01172 #ifdef _XBOX
01173         if(cg.widescreen)
01174                 cg.refdef.fov_x *= 1.125f;
01175 #endif
01176 
01177         return (inwater);
01178 }
01179 
01180 /*
01181 ====================
01182 CG_CalcFov
01183 
01184 Fixed fov at intermissions, otherwise account for fov variable and zooms.
01185 ====================
01186 */
01187 #define WAVE_AMPLITUDE  1
01188 #define WAVE_FREQUENCY  0.4
01189 float zoomFov; //this has to be global client-side
01190 
01191 static int CG_CalcFov( void ) {
01192         float   x;
01193         float   phase;
01194         float   v;
01195         float   fov_x, fov_y;
01196         float   f;
01197         int             inwater;
01198         float   cgFov = cg_fov.value;
01199 
01200         if (cgFov < 1)
01201         {
01202                 cgFov = 1;
01203         }
01204         if (cgFov > 97)
01205         {
01206                 cgFov = 97;
01207         }
01208 
01209         if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
01210                 // if in intermission, use a fixed value
01211                 fov_x = 80;//90;
01212         } else {
01213                 // user selectable
01214                 if ( cgs.dmflags & DF_FIXED_FOV ) {
01215                         // dmflag to prevent wide fov for all clients
01216                         fov_x = 80;//90;
01217                 } else {
01218                         fov_x = cgFov;
01219                         if ( fov_x < 1 ) {
01220                                 fov_x = 1;
01221                         } else if ( fov_x > 160 ) {
01222                                 fov_x = 160;
01223                         }
01224                 }
01225 
01226                 if (cg.predictedPlayerState.zoomMode == 2)
01227                 { //binoculars
01228                         if (zoomFov > 40.0f)
01229                         {
01230                                 zoomFov -= cg.frametime * 0.075f;
01231 
01232                                 if (zoomFov < 40.0f)
01233                                 {
01234                                         zoomFov = 40.0f;
01235                                 }
01236                                 else if (zoomFov > cgFov)
01237                                 {
01238                                         zoomFov = cgFov;
01239                                 }
01240                         }
01241 
01242                         fov_x = zoomFov;
01243                 }
01244                 else if (cg.predictedPlayerState.zoomMode)
01245                 {
01246                         if (!cg.predictedPlayerState.zoomLocked)
01247                         {
01248                                 if (zoomFov > 50)
01249                                 { //Now starting out at nearly half zoomed in
01250                                         zoomFov = 50;
01251                                 }
01252                                 zoomFov -= cg.frametime * 0.035f;//0.075f;
01253 
01254                                 if (zoomFov < MAX_ZOOM_FOV)
01255                                 {
01256                                         zoomFov = MAX_ZOOM_FOV;
01257                                 }
01258                                 else if (zoomFov > cgFov)
01259                                 {
01260                                         zoomFov = cgFov;
01261                                 }
01262                                 else
01263                                 {       // Still zooming
01264                                         static int zoomSoundTime = 0;
01265 
01266                                         if (zoomSoundTime < cg.time || zoomSoundTime > cg.time + 10000)
01267                                         {
01268                                                 trap_S_StartSound(cg.refdef.vieworg, ENTITYNUM_WORLD, CHAN_LOCAL, cgs.media.disruptorZoomLoop);
01269                                                 zoomSoundTime = cg.time + 300;
01270                                         }
01271                                 }
01272                         }
01273 
01274                         if (zoomFov < MAX_ZOOM_FOV)
01275                         {
01276                                 zoomFov = 50;           // hack to fix zoom during vid restart
01277                         }
01278                         fov_x = zoomFov;
01279                 }
01280                 else 
01281                 {
01282                         zoomFov = 80;
01283 
01284                         f = ( cg.time - cg.predictedPlayerState.zoomTime ) / ZOOM_OUT_TIME;
01285                         if ( f > 1.0 ) 
01286                         {
01287                                 fov_x = fov_x;
01288                         } 
01289                         else 
01290                         {
01291                                 fov_x = cg.predictedPlayerState.zoomFov + f * ( fov_x - cg.predictedPlayerState.zoomFov );
01292                         }
01293                 }
01294         }
01295 
01296         x = cg.refdef.width / tan( fov_x / 360 * M_PI );
01297         fov_y = atan2( cg.refdef.height, x );
01298         fov_y = fov_y * 360 / M_PI;
01299 
01300         // warp if underwater
01301         cg.refdef.viewContents = CG_PointContents( cg.refdef.vieworg, -1 );
01302         if ( cg.refdef.viewContents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
01303                 phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
01304                 v = WAVE_AMPLITUDE * sin( phase );
01305                 fov_x += v;
01306                 fov_y -= v;
01307                 inwater = qtrue;
01308         }
01309         else {
01310                 inwater = qfalse;
01311         }
01312 
01313 #ifdef _XBOX
01314         if(cg.widescreen)
01315                 fov_x = fov_y * 1.77777f;
01316 #endif
01317 
01318 
01319         // set it
01320         cg.refdef.fov_x = fov_x;
01321         cg.refdef.fov_y = fov_y;
01322 
01323         if (cg.predictedPlayerState.zoomMode)
01324         {
01325                 cg.zoomSensitivity = zoomFov/cgFov;
01326         }
01327         else if ( !cg.zoomed ) {
01328                 cg.zoomSensitivity = 1;
01329         } else {
01330                 cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
01331         }
01332 
01333         return inwater;
01334 }
01335 
01336 
01337 /*
01338 ===============
01339 CG_DamageBlendBlob
01340 
01341 ===============
01342 */
01343 static void CG_DamageBlendBlob( void ) 
01344 {
01345         int                     t;
01346         int                     maxTime;
01347         refEntity_t             ent;
01348 
01349         if ( !cg.damageValue ) {
01350                 return;
01351         }
01352 
01353         maxTime = DAMAGE_TIME;
01354         t = cg.time - cg.damageTime;
01355         if ( t <= 0 || t >= maxTime ) {
01356                 return;
01357         }
01358 
01359         memset( &ent, 0, sizeof( ent ) );
01360         ent.reType = RT_SPRITE;
01361         ent.renderfx = RF_FIRST_PERSON;
01362 
01363         VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
01364         VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
01365         VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
01366 
01367         ent.radius = cg.damageValue * 3 * ( 1.0 - ((float)t / maxTime) );
01368 
01369         if (cg.snap->ps.damageType == 0)
01370         { //pure health
01371                 ent.customShader = cgs.media.viewPainShader;
01372                 ent.shaderRGBA[0] = 180 * ( 1.0 - ((float)t / maxTime) );
01373                 ent.shaderRGBA[1] = 50 * ( 1.0 - ((float)t / maxTime) );
01374                 ent.shaderRGBA[2] = 50 * ( 1.0 - ((float)t / maxTime) );
01375                 ent.shaderRGBA[3] = 255;
01376         }
01377         else if (cg.snap->ps.damageType == 1)
01378         { //pure shields
01379                 ent.customShader = cgs.media.viewPainShader_Shields;
01380                 ent.shaderRGBA[0] = 50 * ( 1.0 - ((float)t / maxTime) );
01381                 ent.shaderRGBA[1] = 180 * ( 1.0 - ((float)t / maxTime) );
01382                 ent.shaderRGBA[2] = 50 * ( 1.0 - ((float)t / maxTime) );
01383                 ent.shaderRGBA[3] = 255;
01384         }
01385         else
01386         { //shields and health
01387                 ent.customShader = cgs.media.viewPainShader_ShieldsAndHealth;
01388                 ent.shaderRGBA[0] = 180 * ( 1.0 - ((float)t / maxTime) );
01389                 ent.shaderRGBA[1] = 180 * ( 1.0 - ((float)t / maxTime) );
01390                 ent.shaderRGBA[2] = 50 * ( 1.0 - ((float)t / maxTime) );
01391                 ent.shaderRGBA[3] = 255;
01392         }
01393         trap_R_AddRefEntityToScene( &ent );
01394 }
01395 
01396 int cg_actionCamLastTime = 0;
01397 vec3_t cg_actionCamLastPos;
01398 
01399 //action cam routine -rww
01400 static qboolean CG_ThirdPersonActionCam(void)
01401 {
01402     centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
01403         clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum];
01404         trace_t tr;
01405         vec3_t positionDir;
01406         vec3_t desiredAngles;
01407         vec3_t desiredPos;
01408         vec3_t v;
01409         const float smoothFactor = 0.1f*cg_timescale.value;
01410         int i;
01411 
01412         if (!cent->ghoul2)
01413         { //if we don't have a g2 instance this frame for whatever reason then do nothing
01414                 return qfalse;
01415         }
01416 
01417         if (cent->currentState.weapon != WP_SABER)
01418         { //just being safe, should not ever happen
01419                 return qfalse;
01420         }
01421 
01422         if ((cg.time - ci->saber[0].blade[0].trail.lastTime) > 300)
01423         { //too long since we last got the blade position
01424                 return qfalse;
01425         }
01426 
01427         //get direction from base to ent origin
01428         VectorSubtract(ci->saber[0].blade[0].trail.base, cent->lerpOrigin, positionDir);
01429         VectorNormalize(positionDir);
01430 
01431         //position the cam based on the direction and saber position
01432         VectorMA(cent->lerpOrigin, cg_thirdPersonRange.value*2, positionDir, desiredPos);
01433 
01434         //trace to the desired pos to see how far that way we can actually go before we hit something
01435         //the endpos will be valid for our desiredpos no matter what
01436         CG_Trace(&tr, cent->lerpOrigin, NULL, NULL, desiredPos, cent->currentState.number, MASK_SOLID);
01437         VectorCopy(tr.endpos, desiredPos);
01438 
01439         if ((cg.time - cg_actionCamLastTime) > 300)
01440         {
01441                 //do a third person offset first and grab the initial point from that
01442                 CG_OffsetThirdPersonView();
01443                 VectorCopy(cg.refdef.vieworg, cg_actionCamLastPos);
01444         }
01445 
01446         cg_actionCamLastTime = cg.time;
01447 
01448         //lerp the vieworg to the desired pos from the last valid
01449         VectorSubtract(desiredPos, cg_actionCamLastPos, v);
01450         
01451         if (VectorLength(v) > 64.0f)
01452         { //don't bother moving yet if not far from the last pos
01453                 for (i = 0; i < 3; i++)
01454                 {
01455                         cg_actionCamLastPos[i] = (cg_actionCamLastPos[i] + (v[i]*smoothFactor));
01456                         cg.refdef.vieworg[i] = cg_actionCamLastPos[i];
01457                 }
01458         }
01459         else
01460         {
01461                 VectorCopy(cg_actionCamLastPos, cg.refdef.vieworg);
01462         }
01463 
01464         //Make sure the point is alright
01465         CG_Trace(&tr, cent->lerpOrigin, NULL, NULL, cg.refdef.vieworg, cent->currentState.number, MASK_SOLID);
01466         VectorCopy(tr.endpos, cg.refdef.vieworg);
01467 
01468         VectorSubtract(cent->lerpOrigin, cg.refdef.vieworg, positionDir);
01469         vectoangles(positionDir, desiredAngles);
01470 
01471         //just set the angles for now
01472         VectorCopy(desiredAngles, cg.refdef.viewangles);
01473         return qtrue;
01474 }
01475 
01476 vec3_t  cg_lastTurretViewAngles={0};
01477 qboolean CG_CheckPassengerTurretView( void )
01478 {
01479         if ( cg.predictedPlayerState.m_iVehicleNum //in a vehicle
01480                 && cg.predictedPlayerState.generic1 )//as a passenger
01481         {//passenger in a vehicle
01482                 centity_t *vehCent = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
01483                 if ( vehCent->m_pVehicle
01484                         && vehCent->m_pVehicle->m_pVehicleInfo 
01485                         && vehCent->m_pVehicle->m_pVehicleInfo->maxPassengers )
01486                 {//a vehicle capable of carrying passengers
01487                         int turretNum;
01488                         for ( turretNum = 0; turretNum < MAX_VEHICLE_TURRETS; turretNum++ )
01489                         {
01490                                 if ( vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].iAmmoMax )
01491                                 {// valid turret
01492                                         if ( vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].passengerNum == cg.predictedPlayerState.generic1 )
01493                                         {//I control this turret
01494                                                 int boltIndex = -1;
01495                                                 qboolean hackPosAndAngle = qfalse;
01496                                                 if ( vehCent->m_pVehicle->m_iGunnerViewTag[turretNum] != -1 )
01497                                                 {
01498                                                         boltIndex = vehCent->m_pVehicle->m_iGunnerViewTag[turretNum];
01499                                                 }
01500                                                 else
01501                                                 {//crap... guess?
01502                                                         hackPosAndAngle = qtrue;
01503                                                         if ( vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].yawBone )
01504                                                         {
01505                                                                 boltIndex = trap_G2API_AddBolt( vehCent->ghoul2, 0, vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].yawBone );
01506                                                         }
01507                                                         else if ( vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].pitchBone )
01508                                                         {
01509                                                                 boltIndex = trap_G2API_AddBolt( vehCent->ghoul2, 0, vehCent->m_pVehicle->m_pVehicleInfo->turret[turretNum].pitchBone );
01510                                                         }
01511                                                         else
01512                                                         {//well, no way of knowing, so screw it
01513                                                                 return qfalse;
01514                                                         }
01515                                                 }
01516                                                 if ( boltIndex != -1 )
01517                                                 {
01518                                                         mdxaBone_t boltMatrix;
01519                                                         vec3_t fwd, up;
01520                                                         trap_G2API_GetBoltMatrix_NoRecNoRot(vehCent->ghoul2, 0, boltIndex, &boltMatrix, vehCent->lerpAngles,
01521                                                                 vehCent->lerpOrigin, cg.time, NULL, vehCent->modelScale);
01522                                                         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, cg.refdef.vieworg);
01523                                                         if ( hackPosAndAngle )
01524                                                         {
01525                                                                 //FIXME: these are assumptions, externalize?  BETTER YET: give me a controller view bolt/tag for each turret
01526                                                                 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_X, fwd);
01527                                                                 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, up);
01528                                                                 VectorMA( cg.refdef.vieworg, 8.0f, fwd, cg.refdef.vieworg );
01529                                                                 VectorMA( cg.refdef.vieworg, 4.0f, up, cg.refdef.vieworg );
01530                                                         }
01531                                                         else
01532                                                         {
01533                                                                 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, fwd);
01534                                                         }
01535                                                         {
01536                                                                 vec3_t  newAngles, deltaAngles;
01537                                                                 vectoangles( fwd, newAngles );
01538                                                                 AnglesSubtract( newAngles, cg_lastTurretViewAngles, deltaAngles );
01539                                                                 VectorMA( cg_lastTurretViewAngles, 0.5f*(float)cg.frametime/100.0f, deltaAngles, cg.refdef.viewangles );
01540                                                         }
01541                                                         return qtrue;
01542                                                 }
01543                                         }
01544                                 }
01545                         }
01546                 }
01547         }
01548         return qfalse;
01549 }
01550 /*
01551 ===============
01552 CG_CalcViewValues
01553 
01554 Sets cg.refdef view values
01555 ===============
01556 */
01557 void CG_EmplacedView(vec3_t angles);
01558 static int CG_CalcViewValues( void ) {
01559         qboolean manningTurret = qfalse;
01560         playerState_t   *ps;
01561 
01562         memset( &cg.refdef, 0, sizeof( cg.refdef ) );
01563 
01564         // strings for in game rendering
01565         // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) );
01566         // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) );
01567 
01568         // calculate size of 3D view
01569         CG_CalcVrect();
01570 
01571         ps = &cg.predictedPlayerState;
01572 /*
01573         if (cg.cameraMode) {
01574                 vec3_t origin, angles;
01575                 if (trap_getCameraInfo(cg.time, &origin, &angles)) {
01576                         VectorCopy(origin, cg.refdef.vieworg);
01577                         angles[ROLL] = 0;
01578                         VectorCopy(angles, cg.refdef.viewangles);
01579                         AnglesToAxis( cg.refdef.viewangles, cg.refdef.viewaxis );
01580                         return CG_CalcFov();
01581                 } else {
01582                         cg.cameraMode = qfalse;
01583                 }
01584         }
01585 */
01586         // intermission view
01587         if ( ps->pm_type == PM_INTERMISSION ) {
01588                 VectorCopy( ps->origin, cg.refdef.vieworg );
01589                 VectorCopy( ps->viewangles, cg.refdef.viewangles );
01590                 AnglesToAxis( cg.refdef.viewangles, cg.refdef.viewaxis );
01591                 return CG_CalcFov();
01592         }
01593 
01594         cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
01595         cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
01596         cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
01597                 ps->velocity[1] * ps->velocity[1] );
01598 
01599         if (cg.xyspeed > 270)
01600         {
01601                 cg.xyspeed = 270;
01602         }
01603 
01604         manningTurret = CG_CheckPassengerTurretView();
01605         if ( !manningTurret )
01606         {//not manning a turret on a vehicle
01607                 VectorCopy( ps->origin, cg.refdef.vieworg );
01608 #ifdef VEH_CONTROL_SCHEME_4
01609                 if ( cg.predictedPlayerState.m_iVehicleNum )//in a vehicle
01610                 {
01611                         Vehicle_t *pVeh = cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle;
01612                         if ( BG_UnrestrainedPitchRoll( &cg.predictedPlayerState, pVeh ) )//can roll/pitch without restriction
01613                         {//use the vehicle's viewangles to render view!
01614                                 VectorCopy( cg.predictedVehicleState.viewangles, cg.refdef.viewangles );
01615                         }
01616                         else if ( pVeh //valid vehicle data pointer
01617                                 && pVeh->m_pVehicleInfo//valid vehicle info
01618                                 && pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
01619                         {
01620                                 VectorCopy( cg.predictedVehicleState.viewangles, cg.refdef.viewangles );
01621                                 cg.refdef.viewangles[PITCH] = AngleNormalize180( cg.refdef.viewangles[PITCH] );
01622                         }
01623                         else
01624                         {
01625                                 VectorCopy( ps->viewangles, cg.refdef.viewangles );
01626                         }
01627                 }
01628 #else// VEH_CONTROL_SCHEME_4
01629                 if ( cg.predictedPlayerState.m_iVehicleNum //in a vehicle
01630                         && BG_UnrestrainedPitchRoll( &cg.predictedPlayerState, cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle ) )//can roll/pitch without restriction
01631                 {//use the vehicle's viewangles to render view!
01632                         VectorCopy( cg.predictedVehicleState.viewangles, cg.refdef.viewangles );
01633                 }
01634 #endif// VEH_CONTROL_SCHEME_4
01635                 else
01636                 {
01637                         VectorCopy( ps->viewangles, cg.refdef.viewangles );
01638                 }
01639         }
01640         VectorCopy( cg.refdef.viewangles, cg_lastTurretViewAngles );
01641 
01642         if (cg_cameraOrbit.integer) {
01643                 if (cg.time > cg.nextOrbitTime) {
01644                         cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer;
01645                         cg_thirdPersonAngle.value += cg_cameraOrbit.value;
01646                 }
01647         }
01648         // add error decay
01649         if ( cg_errorDecay.value > 0 ) {
01650                 int             t;
01651                 float   f;
01652 
01653                 t = cg.time - cg.predictedErrorTime;
01654                 f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
01655                 if ( f > 0 && f < 1 ) {
01656                         VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
01657                 } else {
01658                         cg.predictedErrorTime = 0;
01659                 }
01660         }
01661 
01662         if (cg.snap->ps.weapon == WP_EMPLACED_GUN &&
01663                 cg.snap->ps.emplacedIndex)
01664         { //constrain the view properly for emplaced guns
01665                 CG_EmplacedView(cg_entities[cg.snap->ps.emplacedIndex].currentState.angles);
01666         }
01667 
01668         if ( !manningTurret )
01669         {
01670                 if ( cg.predictedPlayerState.m_iVehicleNum //in a vehicle
01671                         && BG_UnrestrainedPitchRoll( &cg.predictedPlayerState, cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle ) )//can roll/pitch without restriction
01672                 {//use the vehicle's viewangles to render view!
01673                         CG_OffsetFighterView();
01674                 }
01675                 else if ( cg.renderingThirdPerson ) {
01676                         // back away from character
01677                         if (cg_thirdPersonSpecialCam.integer &&
01678                                 BG_SaberInSpecial(cg.snap->ps.saberMove))
01679                         { //the action cam
01680                                 if (!CG_ThirdPersonActionCam())
01681                                 { //couldn't do it for whatever reason, resort back to third person then
01682                                         CG_OffsetThirdPersonView();
01683                                 }
01684                         }
01685                         else
01686                         {
01687                                 CG_OffsetThirdPersonView();
01688                         }
01689                 } else {
01690                         // offset for local bobbing and kicks
01691                         CG_OffsetFirstPersonView();
01692                 }
01693         }
01694 
01695         // position eye relative to origin
01696         AnglesToAxis( cg.refdef.viewangles, cg.refdef.viewaxis );
01697 
01698         if ( cg.hyperspace ) {
01699                 cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
01700         }
01701 
01702         // field of view
01703         return CG_CalcFov();
01704 }
01705 
01706 
01707 /*
01708 =====================
01709 CG_PowerupTimerSounds
01710 =====================
01711 */
01712 static void CG_PowerupTimerSounds( void ) {
01713         int             i;
01714         int             t;
01715 
01716         // powerup timers going away
01717         for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
01718                 t = cg.snap->ps.powerups[i];
01719                 if ( t <= cg.time ) {
01720                         continue;
01721                 }
01722                 if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
01723                         continue;
01724                 }
01725                 if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) {
01726                         //trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound );
01727                 }
01728         }
01729 }
01730 
01731 /*
01732 ==============
01733 CG_DrawSkyBoxPortal
01734 ==============
01735 */
01736 extern qboolean cg_skyOri;
01737 extern vec3_t cg_skyOriPos;
01738 extern float cg_skyOriScale;
01739 extern qboolean cg_noFogOutsidePortal;
01740 void CG_DrawSkyBoxPortal(const char *cstr)
01741 {
01742         static float lastfov;
01743         refdef_t backuprefdef;
01744         float fov_x;
01745         float fov_y;
01746         float x;
01747         char *token;
01748         float f = 0;
01749 
01750         lastfov = zoomFov;      // for transitions back from zoomed in modes
01751 
01752         backuprefdef = cg.refdef;
01753 
01754         token = COM_ParseExt(&cstr, qfalse);
01755         if (!token || !token[0])
01756         {
01757                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
01758         }
01759         cg.refdef.vieworg[0] = atof(token);
01760 
01761         token = COM_ParseExt(&cstr, qfalse);
01762         if (!token || !token[0])
01763         {
01764                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
01765         }
01766         cg.refdef.vieworg[1] = atof(token);
01767 
01768         token = COM_ParseExt(&cstr, qfalse);
01769         if (!token || !token[0])
01770         {
01771                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
01772         }
01773         cg.refdef.vieworg[2] = atof(token);
01774 
01775         token = COM_ParseExt(&cstr, qfalse);
01776         if (!token || !token[0]) 
01777         {
01778                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring\n");
01779         }
01780         fov_x = atoi(token);
01781 
01782         if (!fov_x)
01783         {
01784                 fov_x = cg_fov.value;
01785         }
01786 
01787         // setup fog the first time, ignore this part of the configstring after that
01788         token = COM_ParseExt(&cstr, qfalse);
01789         if (!token || !token[0])
01790         {
01791                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog state\n");
01792         }
01793         else 
01794         {
01795                 vec4_t  fogColor;
01796                 int             fogStart, fogEnd;
01797 
01798                 if(atoi(token))
01799                 {       // this camera has fog
01800                         token = COM_ParseExt(&cstr, qfalse);
01801 
01802                         if (!token || !token[0])
01803                         {
01804                                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog[0]\n");
01805                         }
01806                         fogColor[0] = atof(token);
01807 
01808                         token = COM_ParseExt(&cstr, qfalse);
01809                         if (!token || !token[0])
01810                         {
01811                                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog[1]\n");
01812                         }
01813                         fogColor[1] = atof(token);
01814 
01815                         token = COM_ParseExt(&cstr, qfalse);
01816                         if (!token || !token[0])
01817                         {
01818                                 CG_Error( "CG_DrawSkyBoxPortal: error parsing skybox configstring.  No fog[2]\n");
01819                         }
01820                         fogColor[2] = atof(token);
01821 
01822                         token = COM_ParseExt(&cstr, qfalse);
01823                         if (!token || !token[0])
01824                         {
01825                                 fogStart = 0;
01826                         }
01827                         else
01828                         {
01829                                 fogStart = atoi(token);
01830                         }
01831 
01832                         token = COM_ParseExt(&cstr, qfalse);
01833                         if (!token || !token[0])
01834                         {
01835                                 fogEnd = 0;
01836                         }
01837                         else
01838                         {
01839                                 fogEnd = atoi(token);
01840                         }
01841                 }
01842         }
01843 
01844         if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION )
01845         {
01846                 // if in intermission, use a fixed value
01847                 fov_x = cg_fov.value;
01848         }
01849         else
01850         {
01851                 fov_x = cg_fov.value;
01852                 if ( fov_x < 1 ) 
01853                 {
01854                         fov_x = 1;
01855                 }
01856                 else if ( fov_x > 160 )
01857                 {
01858                         fov_x = 160;
01859                 }
01860 
01861                 if (cg.predictedPlayerState.zoomMode)
01862                 {
01863                         fov_x = zoomFov;
01864                 }
01865 
01866                 // do smooth transitions for zooming
01867                 if (cg.predictedPlayerState.zoomMode)
01868                 { //zoomed/zooming in
01869                         f = ( cg.time - cg.zoomTime ) / (float)ZOOM_OUT_TIME;
01870                         if ( f > 1.0 ) {
01871                                 fov_x = zoomFov;
01872                         } else {
01873                                 fov_x = fov_x + f * ( zoomFov - fov_x );
01874                         }
01875                         lastfov = fov_x;
01876                 }
01877                 else
01878                 { //zooming out
01879                         f = ( cg.time - cg.zoomTime ) / (float)ZOOM_OUT_TIME;
01880                         if ( f > 1.0 ) {
01881                                 fov_x = fov_x;
01882                         } else {
01883                                 fov_x = zoomFov + f * ( fov_x - zoomFov);
01884                         }
01885                 }
01886         }
01887 
01888         x = cg.refdef.width / tan( fov_x / 360 * M_PI );
01889         fov_y = atan2( cg.refdef.height, x );
01890         fov_y = fov_y * 360 / M_PI;
01891 
01892         cg.refdef.fov_x = fov_x;
01893         cg.refdef.fov_y = fov_y;
01894         
01895         cg.refdef.rdflags |= RDF_SKYBOXPORTAL;
01896         cg.refdef.rdflags |= RDF_DRAWSKYBOX;
01897 
01898         cg.refdef.time = cg.time;
01899 
01900         if ( !cg.hyperspace) 
01901         { //rww - also had to add this to add effects being rendered in portal sky areas properly.
01902                 trap_FX_AddScheduledEffects(qtrue);
01903         }
01904 
01905         CG_AddPacketEntities(qtrue); //rww - There was no proper way to put real entities inside the portal view before.
01906                                                                         //This will put specially flagged entities in the render.
01907 
01908         if (cg_skyOri)
01909         { //ok, we want to orient the sky refdef vieworg based on the normal vieworg's relation to the ori pos
01910                 vec3_t dif;
01911 
01912                 VectorSubtract(backuprefdef.vieworg, cg_skyOriPos, dif);
01913                 VectorScale(dif, cg_skyOriScale, dif);
01914                 VectorAdd(cg.refdef.vieworg, dif, cg.refdef.vieworg);
01915         }
01916 
01917         if (cg_noFogOutsidePortal)
01918         { //make sure no fog flag is stripped first, and make sure it is set on the normal refdef
01919                 cg.refdef.rdflags &= ~RDF_NOFOG;
01920                 backuprefdef.rdflags |= RDF_NOFOG;
01921         }
01922 
01923         // draw the skybox
01924         trap_R_RenderScene( &cg.refdef );
01925 
01926         cg.refdef = backuprefdef;
01927 }
01928 
01929 /*
01930 =====================
01931 CG_AddBufferedSound
01932 =====================
01933 */
01934 void CG_AddBufferedSound( sfxHandle_t sfx ) {
01935         if ( !sfx )
01936                 return;
01937         cg.soundBuffer[cg.soundBufferIn] = sfx;
01938         cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER;
01939         if (cg.soundBufferIn == cg.soundBufferOut) {
01940                 cg.soundBufferOut++;
01941         }
01942 }
01943 
01944 /*
01945 =====================
01946 CG_PlayBufferedSounds
01947 =====================
01948 */
01949 static void CG_PlayBufferedSounds( void ) {
01950         if ( cg.soundTime < cg.time ) {
01951                 if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) {
01952                         trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER);
01953                         cg.soundBuffer[cg.soundBufferOut] = 0;
01954                         cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER;
01955                         cg.soundTime = cg.time + 750;
01956                 }
01957         }
01958 }
01959 
01960 void CG_UpdateSoundTrackers()
01961 {
01962         int num;
01963         centity_t *cent;
01964 
01965         for ( num = 0 ; num < ENTITYNUM_NONE ; num++ )
01966         {
01967                 cent = &cg_entities[num];
01968 
01969                 if (cent && (cent->currentState.eFlags & EF_SOUNDTRACKER) && cent->currentState.number == num)
01970                         //make sure the thing is valid at least.
01971                 { //keep sound for this entity updated in accordance with its attached entity at all times
01972                         if (cg.snap && cent->currentState.trickedentindex == cg.snap->ps.clientNum)
01973                         { //this is actually the player, so center the sound origin right on top of us
01974                                 VectorCopy(cg.refdef.vieworg, cent->lerpOrigin);
01975                                 trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
01976                         }
01977                         else
01978                         {
01979                                 trap_S_UpdateEntityPosition( cent->currentState.number, cg_entities[cent->currentState.trickedentindex].lerpOrigin );
01980                         }
01981                 }
01982 
01983                 if (cent->currentState.number == num)
01984                 {
01985                         //update all looping sounds..
01986                         CG_S_UpdateLoopingSounds(num);
01987                 }
01988         }
01989 }
01990 
01991 //=========================================================================
01992 
01993 /*
01994 ================================
01995 Screen Effect stuff starts here
01996 ================================
01997 */
01998 #define CAMERA_DEFAULT_FOV                      90.0f
01999 #define MAX_SHAKE_INTENSITY                     16.0f
02000 
02001 cgscreffects_t cgScreenEffects;
02002 
02003 void CG_SE_UpdateShake( vec3_t origin, vec3_t angles )
02004 {
02005         vec3_t  moveDir;
02006         float   intensity_scale, intensity;
02007         int             i;
02008 
02009         if ( cgScreenEffects.shake_duration <= 0 )
02010                 return;
02011 
02012         if ( cg.time > ( cgScreenEffects.shake_start + cgScreenEffects.shake_duration ) )
02013         {
02014                 cgScreenEffects.shake_intensity = 0;
02015                 cgScreenEffects.shake_duration = 0;
02016                 cgScreenEffects.shake_start = 0;
02017                 return;
02018         }
02019 
02020         cgScreenEffects.FOV = CAMERA_DEFAULT_FOV;
02021         cgScreenEffects.FOV2 = CAMERA_DEFAULT_FOV;
02022 
02023         //intensity_scale now also takes into account FOV with 90.0 as normal
02024         intensity_scale = 1.0f - ( (float) ( cg.time - cgScreenEffects.shake_start ) / (float) cgScreenEffects.shake_duration ) * (((cgScreenEffects.FOV+cgScreenEffects.FOV2)/2.0f)/90.0f);
02025 
02026         intensity = cgScreenEffects.shake_intensity * intensity_scale;
02027 
02028         for ( i = 0; i < 3; i++ )
02029         {
02030                 moveDir[i] = ( crandom() * intensity );
02031         }
02032 
02033         //Move the camera
02034         VectorAdd( origin, moveDir, origin );
02035 
02036         for ( i=0; i < 2; i++ ) // Don't do ROLL
02037                 moveDir[i] = ( crandom() * intensity );
02038 
02039         //Move the angles
02040         VectorAdd( angles, moveDir, angles );
02041 }
02042 
02043 void CG_SE_UpdateMusic(void)
02044 {
02045         if (cgScreenEffects.music_volume_multiplier < 0.1)
02046         {
02047                 cgScreenEffects.music_volume_multiplier = 1.0;
02048                 return;
02049         }
02050 
02051         if (cgScreenEffects.music_volume_time < cg.time)
02052         {
02053                 if (cgScreenEffects.music_volume_multiplier != 1.0 || cgScreenEffects.music_volume_set)
02054                 {
02055                         char musMultStr[512];
02056 
02057                         cgScreenEffects.music_volume_multiplier += 0.1;
02058                         if (cgScreenEffects.music_volume_multiplier > 1.0)
02059                         {
02060                                 cgScreenEffects.music_volume_multiplier = 1.0;
02061                         }
02062 
02063                         Com_sprintf(musMultStr, sizeof(musMultStr), "%f", cgScreenEffects.music_volume_multiplier);
02064                         trap_Cvar_Set("s_musicMult", musMultStr);
02065 
02066                         if (cgScreenEffects.music_volume_multiplier == 1.0)
02067                         {
02068                                 cgScreenEffects.music_volume_set = qfalse;
02069                         }
02070                         else
02071                         {
02072                                 cgScreenEffects.music_volume_time = cg.time + 200;
02073                         }
02074                 }
02075 
02076                 return;
02077         }
02078 
02079         if (!cgScreenEffects.music_volume_set)
02080         { //if the volume_time is >= cg.time, we should have a volume multiplier set
02081                 char musMultStr[512];
02082 
02083                 Com_sprintf(musMultStr, sizeof(musMultStr), "%f", cgScreenEffects.music_volume_multiplier);
02084                 trap_Cvar_Set("s_musicMult", musMultStr);
02085                 cgScreenEffects.music_volume_set = qtrue;
02086         }
02087 }
02088 
02089 /*
02090 =================
02091 CG_CalcScreenEffects
02092 
02093 Currently just for screen shaking (and music volume management)
02094 =================
02095 */
02096 void CG_CalcScreenEffects(void)
02097 {
02098         CG_SE_UpdateShake(cg.refdef.vieworg, cg.refdef.viewangles);
02099         CG_SE_UpdateMusic();
02100 }
02101 
02102 void CGCam_Shake( float intensity, int duration )
02103 {
02104         if ( intensity > MAX_SHAKE_INTENSITY )
02105                 intensity = MAX_SHAKE_INTENSITY;
02106 
02107         cgScreenEffects.shake_intensity = intensity;
02108         cgScreenEffects.shake_duration = duration;
02109         
02110 
02111         cgScreenEffects.shake_start = cg.time;
02112 //JLFRUMBLE
02113 #ifdef _XBOX
02114 extern void FF_XboxShake(float intensity, int duration);
02115 
02116 FF_XboxShake(intensity, duration);
02117 
02118 #endif
02119 }
02120 
02121 void CG_DoCameraShake( vec3_t origin, float intensity, int radius, int time )
02122 {
02123         //FIXME: When exactly is the vieworg calculated in relation to the rest of the frame?s
02124 
02125         vec3_t  dir;
02126         float   dist, intensityScale;
02127         float   realIntensity;
02128 
02129         VectorSubtract( cg.refdef.vieworg, origin, dir );
02130         dist = VectorNormalize( dir );
02131 
02132         //Use the dir to add kick to the explosion
02133 
02134         if ( dist > radius )
02135                 return;
02136 
02137         intensityScale = 1 - ( dist / (float) radius );
02138         realIntensity = intensity * intensityScale;
02139 
02140         CGCam_Shake( realIntensity, time );
02141 }
02142 
02143 void CGCam_SetMusicMult( float multiplier, int duration )
02144 {
02145         if (multiplier < 0.1f)
02146         {
02147                 multiplier = 0.1f;
02148         }
02149 
02150         if (multiplier > 1.0f)
02151         {
02152                 multiplier = 1.0f;
02153         }
02154 
02155         cgScreenEffects.music_volume_multiplier = multiplier;
02156         cgScreenEffects.music_volume_time = cg.time + duration;
02157         cgScreenEffects.music_volume_set = qfalse;
02158 }
02159 
02160 /*
02161 ================================
02162 Screen Effect stuff ends here
02163 ================================
02164 */
02165 
02166 /*
02167 =================
02168 CG_EmplacedView
02169 
02170 Keep view reasonably constrained in relation to gun -rww
02171 =================
02172 */
02173 #include "../namespace_begin.h"
02174 int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
02175 #include "../namespace_end.h"
02176 
02177 void CG_EmplacedView(vec3_t angles)
02178 {
02179         float yaw;
02180         int override;
02181         
02182         override = BG_EmplacedView(cg.refdef.viewangles, angles, &yaw,
02183                 cg_entities[cg.snap->ps.emplacedIndex].currentState.origin2[0]);
02184         
02185         if (override)
02186         {
02187         cg.refdef.viewangles[YAW] = yaw;
02188                 AnglesToAxis(cg.refdef.viewangles, cg.refdef.viewaxis);
02189 
02190                 if (override == 2)
02191                 {
02192                         trap_SetClientForceAngle(cg.time + 5000, cg.refdef.viewangles);
02193                 }
02194         }
02195 
02196         //we want to constrain the predicted player state viewangles as well
02197         override = BG_EmplacedView(cg.predictedPlayerState.viewangles, angles, &yaw,
02198                 cg_entities[cg.snap->ps.emplacedIndex].currentState.origin2[0]);
02199         if (override)
02200         {
02201         cg.predictedPlayerState.viewangles[YAW] = yaw;
02202         }
02203 }
02204 
02205 //specially add cent's for automap
02206 static void CG_AddRefentForAutoMap(centity_t *cent)
02207 {
02208         refEntity_t ent;
02209         vec3_t flat;
02210 
02211         if (cent->currentState.eFlags & EF_NODRAW)
02212         {
02213                 return;
02214         }
02215 
02216         memset(&ent, 0, sizeof(refEntity_t));
02217         ent.reType = RT_MODEL;
02218 
02219         VectorCopy(cent->lerpAngles, flat);
02220         flat[PITCH] = flat[ROLL] = 0.0f;
02221 
02222         VectorCopy(cent->lerpOrigin, ent.origin);
02223         VectorCopy(flat, ent.angles);
02224         AnglesToAxis(flat, ent.axis);
02225 
02226         if (cent->ghoul2 &&
02227                 (cent->currentState.eType == ET_PLAYER ||
02228                 cent->currentState.eType == ET_NPC ||
02229                 cent->currentState.modelGhoul2))
02230         { //using a ghoul2 model
02231                 ent.ghoul2 = cent->ghoul2;
02232                 ent.radius = cent->currentState.g2radius;
02233 
02234                 if (!ent.radius)
02235                 {
02236                         ent.radius = 64.0f;
02237                 }
02238         }
02239         else
02240         { //then assume a standard indexed model
02241                 ent.hModel = cgs.gameModels[cent->currentState.modelindex];
02242         }
02243 
02244         trap_R_AddRefEntityToScene(&ent);
02245 }
02246 
02247 //add all entities that would be on the radar
02248 void CG_AddRadarAutomapEnts(void)
02249 {
02250         int i = 0;
02251 
02252         //first add yourself
02253         CG_AddRefentForAutoMap(&cg_entities[cg.predictedPlayerState.clientNum]);
02254 
02255         while (i < cg.radarEntityCount)
02256         {
02257                 CG_AddRefentForAutoMap(&cg_entities[cg.radarEntities[i]]);
02258                 i++;
02259         }
02260 }
02261 
02262 /*
02263 ================
02264 CG_DrawAutoMap
02265 
02266 Draws the automap scene. -rww
02267 ================
02268 */
02269 float cg_autoMapZoom = 512.0f;
02270 float cg_autoMapZoomMainOffset = 0.0f;
02271 vec3_t cg_autoMapAngle = {90.0f, 0.0f, 0.0f};
02272 autoMapInput_t cg_autoMapInput;
02273 int cg_autoMapInputTime = 0;
02274 #define SIDEFRAME_WIDTH                 16
02275 #define SIDEFRAME_HEIGHT                32
02276 void CG_DrawAutoMap(void)
02277 {
02278         clientInfo_t    *local;
02279         refdef_t                refdef;
02280         trace_t                 tr;
02281         vec3_t                  fwd;
02282         vec3_t                  playerMins, playerMaxs;
02283         int                             vWidth, vHeight;
02284         float                   hScale, vScale;
02285         float                   x, y, w, h;
02286 
02287         if (!cg_autoMap.integer)
02288         { //don't do anything then
02289                 return;
02290         }
02291 
02292         if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 )
02293         { //don't show when dead
02294                 return;
02295         }
02296 
02297         if ( (cg.predictedPlayerState.pm_flags & PMF_FOLLOW) || cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_SPECTATOR )
02298         { //don't show when spec
02299                 return;
02300         }
02301 
02302         local = &cgs.clientinfo[ cg.predictedPlayerState.clientNum ];
02303         if ( !local->infoValid )
02304         { //don't show if bad ci
02305                 return;
02306         }
02307 
02308         if (cgs.gametype < GT_TEAM)
02309         { //don't show in non-team gametypes
02310                 return;
02311         }
02312 
02313         if (cg_autoMapInputTime >= cg.time)
02314         {
02315                 if (cg_autoMapInput.up)
02316                 {
02317                         cg_autoMapZoom -= cg_autoMapInput.up;
02318                         if (cg_autoMapZoom < cg_autoMapZoomMainOffset+64.0f)
02319                         {
02320                                 cg_autoMapZoom = cg_autoMapZoomMainOffset+64.0f;
02321                         }
02322                 }
02323 
02324                 if (cg_autoMapInput.down)
02325                 {
02326                         cg_autoMapZoom += cg_autoMapInput.down;
02327                         if (cg_autoMapZoom > cg_autoMapZoomMainOffset+4096.0f)
02328                         {
02329                                 cg_autoMapZoom = cg_autoMapZoomMainOffset+4096.0f;
02330                         }
02331                 }
02332 
02333                 if (cg_autoMapInput.yaw)
02334                 {
02335                         cg_autoMapAngle[YAW] += cg_autoMapInput.yaw;
02336                 }
02337 
02338                 if (cg_autoMapInput.pitch)
02339                 {
02340                         cg_autoMapAngle[PITCH] += cg_autoMapInput.pitch;
02341                 }
02342 
02343                 if (cg_autoMapInput.goToDefaults)
02344                 {
02345                         cg_autoMapZoom = 512.0f;
02346                         VectorSet(cg_autoMapAngle, 90.0f, 0.0f, 0.0f);
02347                 }
02348         }
02349 
02350         memset( &refdef, 0, sizeof( refdef ) );
02351 
02352         refdef.rdflags = (RDF_NOWORLDMODEL|RDF_AUTOMAP);
02353 
02354         VectorCopy(cg.predictedPlayerState.origin, refdef.vieworg);
02355         VectorCopy(cg_autoMapAngle, refdef.viewangles);
02356         
02357         //scale out in the direction of the view angles base on the zoom factor
02358         AngleVectors(refdef.viewangles, fwd, 0, 0);
02359         VectorMA(refdef.vieworg, -cg_autoMapZoom, fwd, refdef.vieworg);
02360 
02361         AnglesToAxis(refdef.viewangles, refdef.viewaxis);
02362 
02363         refdef.fov_x = 50;
02364         refdef.fov_y = 50;
02365 
02366         //guess this doesn't need to be done every frame, but eh
02367         trap_R_GetRealRes(&vWidth, &vHeight);
02368 
02369         //set scaling values so that the 640x480 will result at 1.0/1.0
02370         hScale = vWidth/640.0f;
02371         vScale = vHeight/480.0f;
02372 
02373         x = cg_autoMapX.value;
02374         y = cg_autoMapY.value;
02375         w = cg_autoMapW.value;
02376         h = cg_autoMapH.value;
02377 
02378         refdef.x = x*hScale;
02379         refdef.y = y*vScale;
02380         refdef.width = w*hScale;
02381         refdef.height = h*vScale;
02382 
02383         CG_DrawPic(x-SIDEFRAME_WIDTH, y, SIDEFRAME_WIDTH, h, cgs.media.wireframeAutomapFrame_left);
02384         CG_DrawPic(x+w, y, SIDEFRAME_WIDTH, h, cgs.media.wireframeAutomapFrame_right);
02385         CG_DrawPic(x-SIDEFRAME_WIDTH, y-SIDEFRAME_HEIGHT, w+(SIDEFRAME_WIDTH*2), SIDEFRAME_HEIGHT, cgs.media.wireframeAutomapFrame_top);
02386         CG_DrawPic(x-SIDEFRAME_WIDTH, y+h, w+(SIDEFRAME_WIDTH*2), SIDEFRAME_HEIGHT, cgs.media.wireframeAutomapFrame_bottom);
02387 
02388         refdef.time = cg.time;
02389 
02390         trap_R_ClearScene();
02391         CG_AddRadarAutomapEnts();
02392 
02393         if (cg.predictedPlayerState.m_iVehicleNum &&
02394                 cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState.eType == ET_NPC &&
02395                 cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState.NPC_class == CLASS_VEHICLE &&
02396                 cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle &&
02397                 cg_entities[cg.predictedPlayerState.m_iVehicleNum].m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
02398         { //constantly adjust to current height
02399                 trap_R_AutomapElevAdj(cg.predictedPlayerState.origin[2]);
02400         }
02401         else
02402         {
02403                 //Trace down and set the ground elevation as the main automap elevation point
02404                 VectorSet(playerMins, -15, -15, DEFAULT_MINS_2);
02405                 VectorSet(playerMaxs, 15, 15, DEFAULT_MAXS_2);
02406 
02407                 VectorCopy(cg.predictedPlayerState.origin, fwd);
02408                 fwd[2] -= 4096.0f;
02409                 CG_Trace(&tr, cg.predictedPlayerState.origin, playerMins, playerMaxs, fwd, cg.predictedPlayerState.clientNum, MASK_SOLID);
02410 
02411                 if (!tr.startsolid && !tr.allsolid)
02412                 {
02413                         trap_R_AutomapElevAdj(tr.endpos[2]);
02414                 }
02415         }
02416         trap_R_RenderScene( &refdef );
02417 }
02418 
02419 /*
02420 =================
02421 CG_DrawActiveFrame
02422 
02423 Generates and draws a game scene and status information at the given time.
02424 =================
02425 */
02426 static qboolean cg_rangedFogging = qfalse; //so we know if we should go back to normal fog
02427 float cg_linearFogOverride = 0.0f; //designer-specified override for linear fogging style
02428 
02429 #include "../namespace_begin.h"
02430 extern void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride );
02431 extern qboolean PM_InKnockDown( playerState_t *ps );
02432 #include "../namespace_end.h"
02433 
02434 extern qboolean cgQueueLoad;
02435 extern void CG_ActualLoadDeferredPlayers( void );
02436 
02437 static int cg_siegeClassIndex = -2;
02438 
02439 void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) {
02440         int             inwater;
02441         const char *cstr;
02442         float mSensitivity = cg.zoomSensitivity;
02443         float mPitchOverride = 0.0f;
02444         float mYawOverride = 0.0f;
02445         static centity_t *veh = NULL;
02446 #ifdef VEH_CONTROL_SCHEME_4
02447         float mSensitivityOverride = 0.0f;
02448         qboolean bUseFighterPitch = qfalse;
02449         qboolean        isFighter = qfalse;
02450 #endif
02451 
02452         if (cgQueueLoad)
02453         { //do this before you start messing around with adding ghoul2 refents and crap
02454                 CG_ActualLoadDeferredPlayers();
02455                 cgQueueLoad = qfalse;
02456         }
02457 
02458         cg.time = serverTime;
02459         cg.demoPlayback = demoPlayback;
02460 
02461         if (cg.snap && ui_myteam.integer != cg.snap->ps.persistant[PERS_TEAM])
02462         {
02463                 trap_Cvar_Set ( "ui_myteam", va("%i", cg.snap->ps.persistant[PERS_TEAM]) );
02464         }
02465         if (cgs.gametype == GT_SIEGE &&
02466                 cg.snap &&
02467                 cg_siegeClassIndex != cgs.clientinfo[cg.snap->ps.clientNum].siegeIndex)
02468         {
02469                 cg_siegeClassIndex = cgs.clientinfo[cg.snap->ps.clientNum].siegeIndex;
02470                 if (cg_siegeClassIndex == -1)
02471                 {
02472                         trap_Cvar_Set("ui_mySiegeClass", "<none>");
02473                 }
02474                 else
02475                 {
02476                         trap_Cvar_Set("ui_mySiegeClass", bgSiegeClasses[cg_siegeClassIndex].name);
02477                 }
02478         }
02479 
02480         // update cvars
02481         CG_UpdateCvars();
02482 
02483         // if we are only updating the screen as a loading
02484         // pacifier, don't even try to read snapshots
02485         if ( cg.infoScreenText[0] != 0 ) {
02486                 CG_DrawInformation();
02487                 return;
02488         }
02489 
02490         trap_FX_AdjustTime( cg.time );
02491 
02492         CG_RunLightStyles();
02493 
02494         // any looped sounds will be respecified as entities
02495         // are added to the render list
02496         trap_S_ClearLoopingSounds();
02497 
02498         // clear all the render lists
02499         trap_R_ClearScene();
02500 
02501         // set up cg.snap and possibly cg.nextSnap
02502         CG_ProcessSnapshots();
02503 
02504         trap_ROFF_UpdateEntities();
02505 
02506         // if we haven't received any snapshots yet, all
02507         // we can draw is the information screen
02508         if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) )
02509         {
02510 #if 0   
02511                 // Transition from zero to negative one on the snapshot timeout.
02512                 // The reason we do this is because the first client frame is responsible for
02513                 // some farily slow processing (such as weather) and we dont want to include
02514                 // that processing time into our calculations
02515                 if ( !cg.snapshotTimeoutTime )
02516                 {
02517                         cg.snapshotTimeoutTime = -1;
02518                 }
02519                 // Transition the snapshot timeout time from -1 to the current time in 
02520                 // milliseconds which will start the timeout.
02521                 else if ( cg.snapshotTimeoutTime == -1 )
02522                 {               
02523                         cg.snapshotTimeoutTime = trap_Milliseconds ( );
02524                 }
02525 
02526                 // If we have been waiting too long then just error out
02527                 if ( cg.snapshotTimeoutTime > 0 && (trap_Milliseconds ( ) - cg.snapshotTimeoutTime > cg_snapshotTimeout.integer * 1000) )
02528                 {
02529                         Com_Error ( ERR_DROP, CG_GetStringEdString("MP_SVGAME", "SNAPSHOT_TIMEOUT"));
02530                         return;
02531                 }
02532 #endif  
02533                 CG_DrawInformation();
02534                 return;
02535         }
02536 
02537         // let the client system know what our weapon and zoom settings are
02538         if (cg.snap && cg.snap->ps.saberLockTime > cg.time)
02539         {
02540                 mSensitivity = 0.01f;
02541         }
02542         else if (cg.predictedPlayerState.weapon == WP_EMPLACED_GUN)
02543         { //lower sens for emplaced guns and vehicles
02544                 mSensitivity = 0.2f;
02545         }
02546 #ifdef VEH_CONTROL_SCHEME_4
02547         else if (cg.predictedPlayerState.m_iVehicleNum//in a vehicle
02548                 && !cg.predictedPlayerState.generic1 )//not as a passenger
02549         {
02550                 centity_t *cent = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
02551                 if ( cent->m_pVehicle
02552                         && cent->m_pVehicle->m_pVehicleInfo
02553                         && cent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
02554                 {
02555                         BG_VehicleTurnRateForSpeed( cent->m_pVehicle, cent->currentState.speed, &mPitchOverride, &mYawOverride );
02556                         //mSensitivityOverride = 5.0f;//old default value
02557                         mSensitivityOverride = 0.0f;
02558                         bUseFighterPitch = qtrue;
02559                         trap_SetUserCmdValue( cg.weaponSelect, mSensitivity, mPitchOverride, mYawOverride, mSensitivityOverride, cg.forceSelect, cg.itemSelect, bUseFighterPitch );
02560                         isFighter = qtrue;
02561                 }
02562         } 
02563 
02564         if ( !isFighter )
02565 #endif //VEH_CONTROL_SCHEME_4
02566         {
02567                 if (cg.predictedPlayerState.m_iVehicleNum)
02568                 {
02569                         veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
02570                 }
02571                 if (veh &&
02572                         veh->currentState.eType == ET_NPC &&
02573                         veh->currentState.NPC_class == CLASS_VEHICLE &&
02574                         veh->m_pVehicle &&
02575                         veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER &&
02576                         bg_fighterAltControl.integer)
02577                 {
02578                         trap_SetUserCmdValue( cg.weaponSelect, mSensitivity, mPitchOverride, mYawOverride, 0.0f, cg.forceSelect, cg.itemSelect, qtrue );
02579                         veh = NULL; //this is done because I don't want an extra assign each frame because I am so perfect and super efficient.
02580                 }
02581                 else
02582                 {
02583                         trap_SetUserCmdValue( cg.weaponSelect, mSensitivity, mPitchOverride, mYawOverride, 0.0f, cg.forceSelect, cg.itemSelect, qfalse );
02584                 }
02585         }
02586 
02587         // this counter will be bumped for every valid scene we generate
02588         cg.clientFrame++;
02589 
02590         // update cg.predictedPlayerState
02591         CG_PredictPlayerState();
02592 
02593         // decide on third person view
02594         cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
02595 
02596         if (cg.snap->ps.stats[STAT_HEALTH] > 0)
02597         {
02598                 if (cg.predictedPlayerState.weapon == WP_EMPLACED_GUN && cg.predictedPlayerState.emplacedIndex /*&&
02599                         cg_entities[cg.predictedPlayerState.emplacedIndex].currentState.weapon == WP_NONE*/)
02600                 { //force third person for e-web and emplaced use
02601                         cg.renderingThirdPerson = 1;
02602                 }
02603                 else if (cg.predictedPlayerState.weapon == WP_SABER || cg.predictedPlayerState.weapon == WP_MELEE ||
02604                         BG_InGrappleMove(cg.predictedPlayerState.torsoAnim) || BG_InGrappleMove(cg.predictedPlayerState.legsAnim) ||
02605                         cg.predictedPlayerState.forceHandExtend == HANDEXTEND_KNOCKDOWN || cg.predictedPlayerState.fallingToDeath ||
02606                         cg.predictedPlayerState.m_iVehicleNum || PM_InKnockDown(&cg.predictedPlayerState))
02607                 {
02608                         if (cg_fpls.integer && cg.predictedPlayerState.weapon == WP_SABER)
02609                         { //force to first person for fpls
02610                                 cg.renderingThirdPerson = 0;
02611                         }
02612                         else
02613                         {
02614                                 cg.renderingThirdPerson = 1;
02615                         }
02616                 }
02617                 else if (cg.snap->ps.zoomMode)
02618                 { //always force first person when zoomed
02619                         cg.renderingThirdPerson = 0;
02620                 }
02621         }
02622         
02623         if (cg.predictedPlayerState.pm_type == PM_SPECTATOR)
02624         { //always first person for spec
02625                 cg.renderingThirdPerson = 0;
02626         }
02627 
02628 
02629         if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR)
02630         {
02631                 cg.renderingThirdPerson = 0;
02632         }
02633 
02634         // build cg.refdef
02635         inwater = CG_CalcViewValues();
02636 
02637         if (cg_linearFogOverride)
02638         {
02639                 trap_R_SetRangeFog(-cg_linearFogOverride);
02640         }
02641         else if (cg.predictedPlayerState.zoomMode)
02642         { //zooming with binoculars or sniper, set the fog range based on the zoom level -rww
02643                 cg_rangedFogging = qtrue;
02644                 //smaller the fov the less fog we have between the view and cull dist
02645                 trap_R_SetRangeFog(cg.refdef.fov_x*64.0f);
02646         }
02647         else if (cg_rangedFogging)
02648         { //disable it
02649                 cg_rangedFogging = qfalse;
02650                 trap_R_SetRangeFog(0.0f);
02651         }
02652 
02653         cstr = CG_ConfigString(CS_SKYBOXORG);
02654 
02655         if (cstr && cstr[0])
02656         { //we have a skyportal
02657                 CG_DrawSkyBoxPortal(cstr);
02658         }
02659 
02660         CG_CalcScreenEffects();
02661 
02662         // first person blend blobs, done after AnglesToAxis
02663         if ( !cg.renderingThirdPerson && cg.predictedPlayerState.pm_type != PM_SPECTATOR ) {
02664                 CG_DamageBlendBlob();
02665         }
02666 
02667         // build the render lists
02668         if ( !cg.hyperspace ) {
02669                 CG_AddPacketEntities(qfalse);                   // adter calcViewValues, so predicted player state is correct
02670                 CG_AddMarks();
02671                 CG_AddParticles ();
02672                 CG_AddLocalEntities();
02673                 CG_DrawMiscEnts();
02674         }
02675         CG_AddViewWeapon( &cg.predictedPlayerState );
02676 
02677         if ( !cg.hyperspace) 
02678         {
02679                 trap_FX_AddScheduledEffects(qfalse);
02680         }
02681 
02682         // add buffered sounds
02683         CG_PlayBufferedSounds();
02684 
02685         // finish up the rest of the refdef
02686         if ( cg.testModelEntity.hModel ) {
02687                 CG_AddTestModel();
02688         }
02689         cg.refdef.time = cg.time;
02690         memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
02691 
02692         // warning sounds when powerup is wearing off
02693         CG_PowerupTimerSounds();
02694 
02695         // if there are any entities flagged as sound trackers and attached to other entities, update their sound pos
02696         CG_UpdateSoundTrackers();
02697 
02698         if (gCGHasFallVector)
02699         {
02700                 vec3_t lookAng;
02701 
02702                 VectorSubtract(cg.snap->ps.origin, cg.refdef.vieworg, lookAng);
02703                 VectorNormalize(lookAng);
02704                 vectoangles(lookAng, lookAng);
02705 
02706                 VectorCopy(gCGFallVector, cg.refdef.vieworg);
02707                 AnglesToAxis(lookAng, cg.refdef.viewaxis);
02708         }
02709 
02710         //This is done from the vieworg to get origin for non-attenuated sounds
02711         cstr = CG_ConfigString( CS_GLOBAL_AMBIENT_SET );
02712 
02713         if (cstr && cstr[0])
02714         {
02715                 trap_S_UpdateAmbientSet( cstr, cg.refdef.vieworg );
02716         }
02717 
02718         // update audio positions
02719         trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
02720 
02721         // make sure the lagometerSample and frame timing isn't done twice when in stereo
02722         if ( stereoView != STEREO_RIGHT ) {
02723                 cg.frametime = cg.time - cg.oldTime;
02724                 if ( cg.frametime < 0 ) {
02725                         cg.frametime = 0;
02726                 }
02727                 cg.oldTime = cg.time;
02728                 CG_AddLagometerFrameInfo();
02729         }
02730         if (cg_timescale.value != cg_timescaleFadeEnd.value) {
02731                 if (cg_timescale.value < cg_timescaleFadeEnd.value) {
02732                         cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
02733                         if (cg_timescale.value > cg_timescaleFadeEnd.value)
02734                                 cg_timescale.value = cg_timescaleFadeEnd.value;
02735                 }
02736                 else {
02737                         cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
02738                         if (cg_timescale.value < cg_timescaleFadeEnd.value)
02739                                 cg_timescale.value = cg_timescaleFadeEnd.value;
02740                 }
02741                 if (cg_timescaleFadeSpeed.value) {
02742                         trap_Cvar_Set("timescale", va("%f", cg_timescale.value));
02743                 }
02744         }
02745 
02746         // actually issue the rendering calls
02747         CG_DrawActive( stereoView );
02748 
02749         CG_DrawAutoMap();
02750 
02751         if ( cg_stats.integer ) {
02752                 CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
02753         }
02754 }
02755