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