Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

cg_view.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 // cg_view.c -- setup all the parameters (position, angle, etc)
00024 // for a 3D rendering
00025 #include "cg_local.h"
00026 
00027 
00028 /*
00029 =============================================================================
00030 
00031   MODEL TESTING
00032 
00033 The viewthing and gun positioning tools from Q2 have been integrated and
00034 enhanced into a single model testing facility.
00035 
00036 Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
00037 
00038 The names must be the full pathname after the basedir, like 
00039 "models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
00040 
00041 Testmodel will create a fake entity 100 units in front of the current view
00042 position, directly facing the viewer.  It will remain immobile, so you can
00043 move around it to view it from different angles.
00044 
00045 Testgun will cause the model to follow the player around and supress the real
00046 view weapon model.  The default frame 0 of most guns is completely off screen,
00047 so you will probably have to cycle a couple frames to see it.
00048 
00049 "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
00050 frame or skin of the testmodel.  These are bound to F5, F6, F7, and F8 in
00051 q3default.cfg.
00052 
00053 If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
00054 you adjust the positioning.
00055 
00056 Note that none of the model testing features update while the game is paused, so
00057 it may be convenient to test with deathmatch set to 1 so that bringing down the
00058 console doesn't pause the game.
00059 
00060 =============================================================================
00061 */
00062 
00063 /*
00064 =================
00065 CG_TestModel_f
00066 
00067 Creates an entity in front of the current position, which
00068 can then be moved around
00069 =================
00070 */
00071 void CG_TestModel_f (void) {
00072     vec3_t      angles;
00073 
00074     memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
00075     if ( trap_Argc() < 2 ) {
00076         return;
00077     }
00078 
00079     Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
00080     cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
00081 
00082     if ( trap_Argc() == 3 ) {
00083         cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
00084         cg.testModelEntity.frame = 1;
00085         cg.testModelEntity.oldframe = 0;
00086     }
00087     if (! cg.testModelEntity.hModel ) {
00088         CG_Printf( "Can't register model\n" );
00089         return;
00090     }
00091 
00092     VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
00093 
00094     angles[PITCH] = 0;
00095     angles[YAW] = 180 + cg.refdefViewAngles[1];
00096     angles[ROLL] = 0;
00097 
00098     AnglesToAxis( angles, cg.testModelEntity.axis );
00099     cg.testGun = qfalse;
00100 }
00101 
00102 /*
00103 =================
00104 CG_TestGun_f
00105 
00106 Replaces the current view weapon with the given model
00107 =================
00108 */
00109 void CG_TestGun_f (void) {
00110     CG_TestModel_f();
00111     cg.testGun = qtrue;
00112     cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON;
00113 }
00114 
00115 
00116 void CG_TestModelNextFrame_f (void) {
00117     cg.testModelEntity.frame++;
00118     CG_Printf( "frame %i\n", cg.testModelEntity.frame );
00119 }
00120 
00121 void CG_TestModelPrevFrame_f (void) {
00122     cg.testModelEntity.frame--;
00123     if ( cg.testModelEntity.frame < 0 ) {
00124         cg.testModelEntity.frame = 0;
00125     }
00126     CG_Printf( "frame %i\n", cg.testModelEntity.frame );
00127 }
00128 
00129 void CG_TestModelNextSkin_f (void) {
00130     cg.testModelEntity.skinNum++;
00131     CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
00132 }
00133 
00134 void CG_TestModelPrevSkin_f (void) {
00135     cg.testModelEntity.skinNum--;
00136     if ( cg.testModelEntity.skinNum < 0 ) {
00137         cg.testModelEntity.skinNum = 0;
00138     }
00139     CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
00140 }
00141 
00142 static void CG_AddTestModel (void) {
00143     int     i;
00144 
00145     // re-register the model, because the level may have changed
00146     cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
00147     if (! cg.testModelEntity.hModel ) {
00148         CG_Printf ("Can't register model\n");
00149         return;
00150     }
00151 
00152     // if testing a gun, set the origin reletive to the view origin
00153     if ( cg.testGun ) {
00154         VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
00155         VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] );
00156         VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] );
00157         VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] );
00158 
00159         // allow the position to be adjusted
00160         for (i=0 ; i<3 ; i++) {
00161             cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value;
00162             cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value;
00163             cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value;
00164         }
00165     }
00166 
00167     trap_R_AddRefEntityToScene( &cg.testModelEntity );
00168 }
00169 
00170 
00171 
00172 //============================================================================
00173 
00174 
00175 /*
00176 =================
00177 CG_CalcVrect
00178 
00179 Sets the coordinates of the rendered window
00180 =================
00181 */
00182 static void CG_CalcVrect (void) {
00183     int     size;
00184 
00185     // the intermission should allways be full screen
00186     if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
00187         size = 100;
00188     } else {
00189         // bound normal viewsize
00190         if (cg_viewsize.integer < 30) {
00191             trap_Cvar_Set ("cg_viewsize","30");
00192             size = 30;
00193         } else if (cg_viewsize.integer > 100) {
00194             trap_Cvar_Set ("cg_viewsize","100");
00195             size = 100;
00196         } else {
00197             size = cg_viewsize.integer;
00198         }
00199 
00200     }
00201     cg.refdef.width = cgs.glconfig.vidWidth*size/100;
00202     cg.refdef.width &= ~1;
00203 
00204     cg.refdef.height = cgs.glconfig.vidHeight*size/100;
00205     cg.refdef.height &= ~1;
00206 
00207     cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2;
00208     cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2;
00209 }
00210 
00211 //==============================================================================
00212 
00213 
00214 /*
00215 ===============
00216 CG_OffsetThirdPersonView
00217 
00218 ===============
00219 */
00220 #define FOCUS_DISTANCE  512
00221 static void CG_OffsetThirdPersonView( void ) {
00222     vec3_t      forward, right, up;
00223     vec3_t      view;
00224     vec3_t      focusAngles;
00225     trace_t     trace;
00226     static vec3_t   mins = { -4, -4, -4 };
00227     static vec3_t   maxs = { 4, 4, 4 };
00228     vec3_t      focusPoint;
00229     float       focusDist;
00230     float       forwardScale, sideScale;
00231 
00232     cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;
00233 
00234     VectorCopy( cg.refdefViewAngles, focusAngles );
00235 
00236     // if dead, look at killer
00237     if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
00238         focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
00239         cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
00240     }
00241 
00242     if ( focusAngles[PITCH] > 45 ) {
00243         focusAngles[PITCH] = 45;        // don't go too far overhead
00244     }
00245     AngleVectors( focusAngles, forward, NULL, NULL );
00246 
00247     VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
00248 
00249     VectorCopy( cg.refdef.vieworg, view );
00250 
00251     view[2] += 8;
00252 
00253     cg.refdefViewAngles[PITCH] *= 0.5;
00254 
00255     AngleVectors( cg.refdefViewAngles, forward, right, up );
00256 
00257     forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
00258     sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
00259     VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
00260     VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
00261 
00262     // trace a ray from the origin to the viewpoint to make sure the view isn't
00263     // in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything
00264 
00265     if (!cg_cameraMode.integer) {
00266         CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
00267 
00268         if ( trace.fraction != 1.0 ) {
00269             VectorCopy( trace.endpos, view );
00270             view[2] += (1.0 - trace.fraction) * 32;
00271             // try another trace to this position, because a tunnel may have the ceiling
00272             // close enogh that this is poking out
00273 
00274             CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
00275             VectorCopy( trace.endpos, view );
00276         }
00277     }
00278 
00279 
00280     VectorCopy( view, cg.refdef.vieworg );
00281 
00282     // select pitch to look at focus point from vieword
00283     VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
00284     focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
00285     if ( focusDist < 1 ) {
00286         focusDist = 1;  // should never happen
00287     }
00288     cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
00289     cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
00290 }
00291 
00292 
00293 // this causes a compiler bug on mac MrC compiler
00294 static void CG_StepOffset( void ) {
00295     int     timeDelta;
00296     
00297     // smooth out stair climbing
00298     timeDelta = cg.time - cg.stepTime;
00299     if ( timeDelta < STEP_TIME ) {
00300         cg.refdef.vieworg[2] -= cg.stepChange 
00301             * (STEP_TIME - timeDelta) / STEP_TIME;
00302     }
00303 }
00304 
00305 /*
00306 ===============
00307 CG_OffsetFirstPersonView
00308 
00309 ===============
00310 */
00311 static void CG_OffsetFirstPersonView( void ) {
00312     float           *origin;
00313     float           *angles;
00314     float           bob;
00315     float           ratio;
00316     float           delta;
00317     float           speed;
00318     float           f;
00319     vec3_t          predictedVelocity;
00320     int             timeDelta;
00321     
00322     if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
00323         return;
00324     }
00325 
00326     origin = cg.refdef.vieworg;
00327     angles = cg.refdefViewAngles;
00328 
00329     // if dead, fix the angle and don't add any kick
00330     if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
00331         angles[ROLL] = 40;
00332         angles[PITCH] = -15;
00333         angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
00334         origin[2] += cg.predictedPlayerState.viewheight;
00335         return;
00336     }
00337 
00338     // add angles based on weapon kick
00339     VectorAdd (angles, cg.kick_angles, angles);
00340 
00341     // add angles based on damage kick
00342     if ( cg.damageTime ) {
00343         ratio = cg.time - cg.damageTime;
00344         if ( ratio < DAMAGE_DEFLECT_TIME ) {
00345             ratio /= DAMAGE_DEFLECT_TIME;
00346             angles[PITCH] += ratio * cg.v_dmg_pitch;
00347             angles[ROLL] += ratio * cg.v_dmg_roll;
00348         } else {
00349             ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
00350             if ( ratio > 0 ) {
00351                 angles[PITCH] += ratio * cg.v_dmg_pitch;
00352                 angles[ROLL] += ratio * cg.v_dmg_roll;
00353             }
00354         }
00355     }
00356 
00357     // add pitch based on fall kick
00358 #if 0
00359     ratio = ( cg.time - cg.landTime) / FALL_TIME;
00360     if (ratio < 0)
00361         ratio = 0;
00362     angles[PITCH] += ratio * cg.fall_value;
00363 #endif
00364 
00365     // add angles based on velocity
00366     VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );
00367 
00368     delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
00369     angles[PITCH] += delta * cg_runpitch.value;
00370     
00371     delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
00372     angles[ROLL] -= delta * cg_runroll.value;
00373 
00374     // add angles based on bob
00375 
00376     // make sure the bob is visible even at low speeds
00377     speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
00378 
00379     delta = cg.bobfracsin * cg_bobpitch.value * speed;
00380     if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
00381         delta *= 3;     // crouching
00382     angles[PITCH] += delta;
00383     delta = cg.bobfracsin * cg_bobroll.value * speed;
00384     if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
00385         delta *= 3;     // crouching accentuates roll
00386     if (cg.bobcycle & 1)
00387         delta = -delta;
00388     angles[ROLL] += delta;
00389 
00390 //===================================
00391 
00392     // add view height
00393     origin[2] += cg.predictedPlayerState.viewheight;
00394 
00395     // smooth out duck height changes
00396     timeDelta = cg.time - cg.duckTime;
00397     if ( timeDelta < DUCK_TIME) {
00398         cg.refdef.vieworg[2] -= cg.duckChange 
00399             * (DUCK_TIME - timeDelta) / DUCK_TIME;
00400     }
00401 
00402     // add bob height
00403     bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
00404     if (bob > 6) {
00405         bob = 6;
00406     }
00407 
00408     origin[2] += bob;
00409 
00410 
00411     // add fall height
00412     delta = cg.time - cg.landTime;
00413     if ( delta < LAND_DEFLECT_TIME ) {
00414         f = delta / LAND_DEFLECT_TIME;
00415         cg.refdef.vieworg[2] += cg.landChange * f;
00416     } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
00417         delta -= LAND_DEFLECT_TIME;
00418         f = 1.0 - ( delta / LAND_RETURN_TIME );
00419         cg.refdef.vieworg[2] += cg.landChange * f;
00420     }
00421 
00422     // add step offset
00423     CG_StepOffset();
00424 
00425     // add kick offset
00426 
00427     VectorAdd (origin, cg.kick_origin, origin);
00428 
00429     // pivot the eye based on a neck length
00430 #if 0
00431     {
00432 #define NECK_LENGTH     8
00433     vec3_t          forward, up;
00434  
00435     cg.refdef.vieworg[2] -= NECK_LENGTH;
00436     AngleVectors( cg.refdefViewAngles, forward, NULL, up );
00437     VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
00438     VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
00439     }
00440 #endif
00441 }
00442 
00443 //======================================================================
00444 
00445 void CG_ZoomDown_f( void ) { 
00446     if ( cg.zoomed ) {
00447         return;
00448     }
00449     cg.zoomed = qtrue;
00450     cg.zoomTime = cg.time;
00451 }
00452 
00453 void CG_ZoomUp_f( void ) { 
00454     if ( !cg.zoomed ) {
00455         return;
00456     }
00457     cg.zoomed = qfalse;
00458     cg.zoomTime = cg.time;
00459 }
00460 
00461 
00462 /*
00463 ====================
00464 CG_CalcFov
00465 
00466 Fixed fov at intermissions, otherwise account for fov variable and zooms.
00467 ====================
00468 */
00469 #define WAVE_AMPLITUDE  1
00470 #define WAVE_FREQUENCY  0.4
00471 
00472 static int CG_CalcFov( void ) {
00473     float   x;
00474     float   phase;
00475     float   v;
00476     int     contents;
00477     float   fov_x, fov_y;
00478     float   zoomFov;
00479     float   f;
00480     int     inwater;
00481 
00482     if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
00483         // if in intermission, use a fixed value
00484         fov_x = 90;
00485     } else {
00486         // user selectable
00487         if ( cgs.dmflags & DF_FIXED_FOV ) {
00488             // dmflag to prevent wide fov for all clients
00489             fov_x = 90;
00490         } else {
00491             fov_x = cg_fov.value;
00492             if ( fov_x < 1 ) {
00493                 fov_x = 1;
00494             } else if ( fov_x > 160 ) {
00495                 fov_x = 160;
00496             }
00497         }
00498 
00499         // account for zooms
00500         zoomFov = cg_zoomFov.value;
00501         if ( zoomFov < 1 ) {
00502             zoomFov = 1;
00503         } else if ( zoomFov > 160 ) {
00504             zoomFov = 160;
00505         }
00506 
00507         if ( cg.zoomed ) {
00508             f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
00509             if ( f > 1.0 ) {
00510                 fov_x = zoomFov;
00511             } else {
00512                 fov_x = fov_x + f * ( zoomFov - fov_x );
00513             }
00514         } else {
00515             f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
00516             if ( f > 1.0 ) {
00517                 fov_x = fov_x;
00518             } else {
00519                 fov_x = zoomFov + f * ( fov_x - zoomFov );
00520             }
00521         }
00522     }
00523 
00524     x = cg.refdef.width / tan( fov_x / 360 * M_PI );
00525     fov_y = atan2( cg.refdef.height, x );
00526     fov_y = fov_y * 360 / M_PI;
00527 
00528     // warp if underwater
00529     contents = CG_PointContents( cg.refdef.vieworg, -1 );
00530     if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
00531         phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
00532         v = WAVE_AMPLITUDE * sin( phase );
00533         fov_x += v;
00534         fov_y -= v;
00535         inwater = qtrue;
00536     }
00537     else {
00538         inwater = qfalse;
00539     }
00540 
00541 
00542     // set it
00543     cg.refdef.fov_x = fov_x;
00544     cg.refdef.fov_y = fov_y;
00545 
00546     if ( !cg.zoomed ) {
00547         cg.zoomSensitivity = 1;
00548     } else {
00549         cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
00550     }
00551 
00552     return inwater;
00553 }
00554 
00555 
00556 
00557 /*
00558 ===============
00559 CG_DamageBlendBlob
00560 
00561 ===============
00562 */
00563 static void CG_DamageBlendBlob( void ) {
00564     int         t;
00565     int         maxTime;
00566     refEntity_t     ent;
00567 
00568     if ( !cg.damageValue ) {
00569         return;
00570     }
00571 
00572     //if (cg.cameraMode) {
00573     //  return;
00574     //}
00575 
00576     // ragePro systems can't fade blends, so don't obscure the screen
00577     if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
00578         return;
00579     }
00580 
00581     maxTime = DAMAGE_TIME;
00582     t = cg.time - cg.damageTime;
00583     if ( t <= 0 || t >= maxTime ) {
00584         return;
00585     }
00586 
00587 
00588     memset( &ent, 0, sizeof( ent ) );
00589     ent.reType = RT_SPRITE;
00590     ent.renderfx = RF_FIRST_PERSON;
00591 
00592     VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
00593     VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
00594     VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
00595 
00596     ent.radius = cg.damageValue * 3;
00597     ent.customShader = cgs.media.viewBloodShader;
00598     ent.shaderRGBA[0] = 255;
00599     ent.shaderRGBA[1] = 255;
00600     ent.shaderRGBA[2] = 255;
00601     ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) );
00602     trap_R_AddRefEntityToScene( &ent );
00603 }
00604 
00605 
00606 /*
00607 ===============
00608 CG_CalcViewValues
00609 
00610 Sets cg.refdef view values
00611 ===============
00612 */
00613 static int CG_CalcViewValues( void ) {
00614     playerState_t   *ps;
00615 
00616     memset( &cg.refdef, 0, sizeof( cg.refdef ) );
00617 
00618     // strings for in game rendering
00619     // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) );
00620     // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) );
00621 
00622     // calculate size of 3D view
00623     CG_CalcVrect();
00624 
00625     ps = &cg.predictedPlayerState;
00626 /*
00627     if (cg.cameraMode) {
00628         vec3_t origin, angles;
00629         if (trap_getCameraInfo(cg.time, &origin, &angles)) {
00630             VectorCopy(origin, cg.refdef.vieworg);
00631             angles[ROLL] = 0;
00632             VectorCopy(angles, cg.refdefViewAngles);
00633             AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
00634             return CG_CalcFov();
00635         } else {
00636             cg.cameraMode = qfalse;
00637         }
00638     }
00639 */
00640     // intermission view
00641     if ( ps->pm_type == PM_INTERMISSION ) {
00642         VectorCopy( ps->origin, cg.refdef.vieworg );
00643         VectorCopy( ps->viewangles, cg.refdefViewAngles );
00644         AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
00645         return CG_CalcFov();
00646     }
00647 
00648     cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
00649     cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
00650     cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
00651         ps->velocity[1] * ps->velocity[1] );
00652 
00653 
00654     VectorCopy( ps->origin, cg.refdef.vieworg );
00655     VectorCopy( ps->viewangles, cg.refdefViewAngles );
00656 
00657     if (cg_cameraOrbit.integer) {
00658         if (cg.time > cg.nextOrbitTime) {
00659             cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer;
00660             cg_thirdPersonAngle.value += cg_cameraOrbit.value;
00661         }
00662     }
00663     // add error decay
00664     if ( cg_errorDecay.value > 0 ) {
00665         int     t;
00666         float   f;
00667 
00668         t = cg.time - cg.predictedErrorTime;
00669         f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
00670         if ( f > 0 && f < 1 ) {
00671             VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
00672         } else {
00673             cg.predictedErrorTime = 0;
00674         }
00675     }
00676 
00677     if ( cg.renderingThirdPerson ) {
00678         // back away from character
00679         CG_OffsetThirdPersonView();
00680     } else {
00681         // offset for local bobbing and kicks
00682         CG_OffsetFirstPersonView();
00683     }
00684 
00685     // position eye reletive to origin
00686     AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
00687 
00688     if ( cg.hyperspace ) {
00689         cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
00690     }
00691 
00692     // field of view
00693     return CG_CalcFov();
00694 }
00695 
00696 
00697 /*
00698 =====================
00699 CG_PowerupTimerSounds
00700 =====================
00701 */
00702 static void CG_PowerupTimerSounds( void ) {
00703     int     i;
00704     int     t;
00705 
00706     // powerup timers going away
00707     for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
00708         t = cg.snap->ps.powerups[i];
00709         if ( t <= cg.time ) {
00710             continue;
00711         }
00712         if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
00713             continue;
00714         }
00715         if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) {
00716             trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound );
00717         }
00718     }
00719 }
00720 
00721 /*
00722 =====================
00723 CG_AddBufferedSound
00724 =====================
00725 */
00726 void CG_AddBufferedSound( sfxHandle_t sfx ) {
00727     if ( !sfx )
00728         return;
00729     cg.soundBuffer[cg.soundBufferIn] = sfx;
00730     cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER;
00731     if (cg.soundBufferIn == cg.soundBufferOut) {
00732         cg.soundBufferOut++;
00733     }
00734 }
00735 
00736 /*
00737 =====================
00738 CG_PlayBufferedSounds
00739 =====================
00740 */
00741 static void CG_PlayBufferedSounds( void ) {
00742     if ( cg.soundTime < cg.time ) {
00743         if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) {
00744             trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER);
00745             cg.soundBuffer[cg.soundBufferOut] = 0;
00746             cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER;
00747             cg.soundTime = cg.time + 750;
00748         }
00749     }
00750 }
00751 
00752 //=========================================================================
00753 
00754 /*
00755 =================
00756 CG_DrawActiveFrame
00757 
00758 Generates and draws a game scene and status information at the given time.
00759 =================
00760 */
00761 void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) {
00762     int     inwater;
00763 
00764     cg.time = serverTime;
00765     cg.demoPlayback = demoPlayback;
00766 
00767     // update cvars
00768     CG_UpdateCvars();
00769 
00770     // if we are only updating the screen as a loading
00771     // pacifier, don't even try to read snapshots
00772     if ( cg.infoScreenText[0] != 0 ) {
00773         CG_DrawInformation();
00774         return;
00775     }
00776 
00777     // any looped sounds will be respecified as entities
00778     // are added to the render list
00779     trap_S_ClearLoopingSounds(qfalse);
00780 
00781     // clear all the render lists
00782     trap_R_ClearScene();
00783 
00784     // set up cg.snap and possibly cg.nextSnap
00785     CG_ProcessSnapshots();
00786 
00787     // if we haven't received any snapshots yet, all
00788     // we can draw is the information screen
00789     if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
00790         CG_DrawInformation();
00791         return;
00792     }
00793 
00794     // let the client system know what our weapon and zoom settings are
00795     trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity );
00796 
00797     // this counter will be bumped for every valid scene we generate
00798     cg.clientFrame++;
00799 
00800     // update cg.predictedPlayerState
00801     CG_PredictPlayerState();
00802 
00803     // decide on third person view
00804     cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
00805 
00806     // build cg.refdef
00807     inwater = CG_CalcViewValues();
00808 
00809     // first person blend blobs, done after AnglesToAxis
00810     if ( !cg.renderingThirdPerson ) {
00811         CG_DamageBlendBlob();
00812     }
00813 
00814     // build the render lists
00815     if ( !cg.hyperspace ) {
00816         CG_AddPacketEntities();         // adter calcViewValues, so predicted player state is correct
00817         CG_AddMarks();
00818         CG_AddParticles ();
00819         CG_AddLocalEntities();
00820     }
00821     CG_AddViewWeapon( &cg.predictedPlayerState );
00822 
00823     // add buffered sounds
00824     CG_PlayBufferedSounds();
00825 
00826     // play buffered voice chats
00827     CG_PlayBufferedVoiceChats();
00828 
00829     // finish up the rest of the refdef
00830     if ( cg.testModelEntity.hModel ) {
00831         CG_AddTestModel();
00832     }
00833     cg.refdef.time = cg.time;
00834     memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
00835 
00836     // warning sounds when powerup is wearing off
00837     CG_PowerupTimerSounds();
00838 
00839     // update audio positions
00840     trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
00841 
00842     // make sure the lagometerSample and frame timing isn't done twice when in stereo
00843     if ( stereoView != STEREO_RIGHT ) {
00844         cg.frametime = cg.time - cg.oldTime;
00845         if ( cg.frametime < 0 ) {
00846             cg.frametime = 0;
00847         }
00848         cg.oldTime = cg.time;
00849         CG_AddLagometerFrameInfo();
00850     }
00851     if (cg_timescale.value != cg_timescaleFadeEnd.value) {
00852         if (cg_timescale.value < cg_timescaleFadeEnd.value) {
00853             cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
00854             if (cg_timescale.value > cg_timescaleFadeEnd.value)
00855                 cg_timescale.value = cg_timescaleFadeEnd.value;
00856         }
00857         else {
00858             cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
00859             if (cg_timescale.value < cg_timescaleFadeEnd.value)
00860                 cg_timescale.value = cg_timescaleFadeEnd.value;
00861         }
00862         if (cg_timescaleFadeSpeed.value) {
00863             trap_Cvar_Set("timescale", va("%f", cg_timescale.value));
00864         }
00865     }
00866 
00867     // actually issue the rendering calls
00868     CG_DrawActive( stereoView );
00869 
00870     if ( cg_stats.integer ) {
00871         CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
00872     }
00873 
00874 
00875 }
00876 

Generated on Thu Aug 25 12:37:25 2005 for Quake III Arena by  doxygen 1.3.9.1