Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cg_view.c (22642B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 // cg_view.c -- setup all the parameters (position, angle, etc)
     24 // for a 3D rendering
     25 #include "cg_local.h"
     26 
     27 
     28 /*
     29 =============================================================================
     30 
     31   MODEL TESTING
     32 
     33 The viewthing and gun positioning tools from Q2 have been integrated and
     34 enhanced into a single model testing facility.
     35 
     36 Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
     37 
     38 The names must be the full pathname after the basedir, like 
     39 "models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
     40 
     41 Testmodel will create a fake entity 100 units in front of the current view
     42 position, directly facing the viewer.  It will remain immobile, so you can
     43 move around it to view it from different angles.
     44 
     45 Testgun will cause the model to follow the player around and supress the real
     46 view weapon model.  The default frame 0 of most guns is completely off screen,
     47 so you will probably have to cycle a couple frames to see it.
     48 
     49 "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
     50 frame or skin of the testmodel.  These are bound to F5, F6, F7, and F8 in
     51 q3default.cfg.
     52 
     53 If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
     54 you adjust the positioning.
     55 
     56 Note that none of the model testing features update while the game is paused, so
     57 it may be convenient to test with deathmatch set to 1 so that bringing down the
     58 console doesn't pause the game.
     59 
     60 =============================================================================
     61 */
     62 
     63 /*
     64 =================
     65 CG_TestModel_f
     66 
     67 Creates an entity in front of the current position, which
     68 can then be moved around
     69 =================
     70 */
     71 void CG_TestModel_f (void) {
     72 	vec3_t		angles;
     73 
     74 	memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
     75 	if ( trap_Argc() < 2 ) {
     76 		return;
     77 	}
     78 
     79 	Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
     80 	cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
     81 
     82 	if ( trap_Argc() == 3 ) {
     83 		cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
     84 		cg.testModelEntity.frame = 1;
     85 		cg.testModelEntity.oldframe = 0;
     86 	}
     87 	if (! cg.testModelEntity.hModel ) {
     88 		CG_Printf( "Can't register model\n" );
     89 		return;
     90 	}
     91 
     92 	VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
     93 
     94 	angles[PITCH] = 0;
     95 	angles[YAW] = 180 + cg.refdefViewAngles[1];
     96 	angles[ROLL] = 0;
     97 
     98 	AnglesToAxis( angles, cg.testModelEntity.axis );
     99 	cg.testGun = qfalse;
    100 }
    101 
    102 /*
    103 =================
    104 CG_TestGun_f
    105 
    106 Replaces the current view weapon with the given model
    107 =================
    108 */
    109 void CG_TestGun_f (void) {
    110 	CG_TestModel_f();
    111 	cg.testGun = qtrue;
    112 	cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON;
    113 }
    114 
    115 
    116 void CG_TestModelNextFrame_f (void) {
    117 	cg.testModelEntity.frame++;
    118 	CG_Printf( "frame %i\n", cg.testModelEntity.frame );
    119 }
    120 
    121 void CG_TestModelPrevFrame_f (void) {
    122 	cg.testModelEntity.frame--;
    123 	if ( cg.testModelEntity.frame < 0 ) {
    124 		cg.testModelEntity.frame = 0;
    125 	}
    126 	CG_Printf( "frame %i\n", cg.testModelEntity.frame );
    127 }
    128 
    129 void CG_TestModelNextSkin_f (void) {
    130 	cg.testModelEntity.skinNum++;
    131 	CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
    132 }
    133 
    134 void CG_TestModelPrevSkin_f (void) {
    135 	cg.testModelEntity.skinNum--;
    136 	if ( cg.testModelEntity.skinNum < 0 ) {
    137 		cg.testModelEntity.skinNum = 0;
    138 	}
    139 	CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
    140 }
    141 
    142 static void CG_AddTestModel (void) {
    143 	int		i;
    144 
    145 	// re-register the model, because the level may have changed
    146 	cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
    147 	if (! cg.testModelEntity.hModel ) {
    148 		CG_Printf ("Can't register model\n");
    149 		return;
    150 	}
    151 
    152 	// if testing a gun, set the origin reletive to the view origin
    153 	if ( cg.testGun ) {
    154 		VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
    155 		VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] );
    156 		VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] );
    157 		VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] );
    158 
    159 		// allow the position to be adjusted
    160 		for (i=0 ; i<3 ; i++) {
    161 			cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value;
    162 			cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value;
    163 			cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value;
    164 		}
    165 	}
    166 
    167 	trap_R_AddRefEntityToScene( &cg.testModelEntity );
    168 }
    169 
    170 
    171 
    172 //============================================================================
    173 
    174 
    175 /*
    176 =================
    177 CG_CalcVrect
    178 
    179 Sets the coordinates of the rendered window
    180 =================
    181 */
    182 static void CG_CalcVrect (void) {
    183 	int		size;
    184 
    185 	// the intermission should allways be full screen
    186 	if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
    187 		size = 100;
    188 	} else {
    189 		// bound normal viewsize
    190 		if (cg_viewsize.integer < 30) {
    191 			trap_Cvar_Set ("cg_viewsize","30");
    192 			size = 30;
    193 		} else if (cg_viewsize.integer > 100) {
    194 			trap_Cvar_Set ("cg_viewsize","100");
    195 			size = 100;
    196 		} else {
    197 			size = cg_viewsize.integer;
    198 		}
    199 
    200 	}
    201 	cg.refdef.width = cgs.glconfig.vidWidth*size/100;
    202 	cg.refdef.width &= ~1;
    203 
    204 	cg.refdef.height = cgs.glconfig.vidHeight*size/100;
    205 	cg.refdef.height &= ~1;
    206 
    207 	cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2;
    208 	cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2;
    209 }
    210 
    211 //==============================================================================
    212 
    213 
    214 /*
    215 ===============
    216 CG_OffsetThirdPersonView
    217 
    218 ===============
    219 */
    220 #define	FOCUS_DISTANCE	512
    221 static void CG_OffsetThirdPersonView( void ) {
    222 	vec3_t		forward, right, up;
    223 	vec3_t		view;
    224 	vec3_t		focusAngles;
    225 	trace_t		trace;
    226 	static vec3_t	mins = { -4, -4, -4 };
    227 	static vec3_t	maxs = { 4, 4, 4 };
    228 	vec3_t		focusPoint;
    229 	float		focusDist;
    230 	float		forwardScale, sideScale;
    231 
    232 	cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;
    233 
    234 	VectorCopy( cg.refdefViewAngles, focusAngles );
    235 
    236 	// if dead, look at killer
    237 	if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
    238 		focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
    239 		cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
    240 	}
    241 
    242 	if ( focusAngles[PITCH] > 45 ) {
    243 		focusAngles[PITCH] = 45;		// don't go too far overhead
    244 	}
    245 	AngleVectors( focusAngles, forward, NULL, NULL );
    246 
    247 	VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
    248 
    249 	VectorCopy( cg.refdef.vieworg, view );
    250 
    251 	view[2] += 8;
    252 
    253 	cg.refdefViewAngles[PITCH] *= 0.5;
    254 
    255 	AngleVectors( cg.refdefViewAngles, forward, right, up );
    256 
    257 	forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
    258 	sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
    259 	VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
    260 	VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
    261 
    262 	// trace a ray from the origin to the viewpoint to make sure the view isn't
    263 	// in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything
    264 
    265 	if (!cg_cameraMode.integer) {
    266 		CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
    267 
    268 		if ( trace.fraction != 1.0 ) {
    269 			VectorCopy( trace.endpos, view );
    270 			view[2] += (1.0 - trace.fraction) * 32;
    271 			// try another trace to this position, because a tunnel may have the ceiling
    272 			// close enogh that this is poking out
    273 
    274 			CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
    275 			VectorCopy( trace.endpos, view );
    276 		}
    277 	}
    278 
    279 
    280 	VectorCopy( view, cg.refdef.vieworg );
    281 
    282 	// select pitch to look at focus point from vieword
    283 	VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
    284 	focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
    285 	if ( focusDist < 1 ) {
    286 		focusDist = 1;	// should never happen
    287 	}
    288 	cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
    289 	cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
    290 }
    291 
    292 
    293 // this causes a compiler bug on mac MrC compiler
    294 static void CG_StepOffset( void ) {
    295 	int		timeDelta;
    296 	
    297 	// smooth out stair climbing
    298 	timeDelta = cg.time - cg.stepTime;
    299 	if ( timeDelta < STEP_TIME ) {
    300 		cg.refdef.vieworg[2] -= cg.stepChange 
    301 			* (STEP_TIME - timeDelta) / STEP_TIME;
    302 	}
    303 }
    304 
    305 /*
    306 ===============
    307 CG_OffsetFirstPersonView
    308 
    309 ===============
    310 */
    311 static void CG_OffsetFirstPersonView( void ) {
    312 	float			*origin;
    313 	float			*angles;
    314 	float			bob;
    315 	float			ratio;
    316 	float			delta;
    317 	float			speed;
    318 	float			f;
    319 	vec3_t			predictedVelocity;
    320 	int				timeDelta;
    321 	
    322 	if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
    323 		return;
    324 	}
    325 
    326 	origin = cg.refdef.vieworg;
    327 	angles = cg.refdefViewAngles;
    328 
    329 	// if dead, fix the angle and don't add any kick
    330 	if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
    331 		angles[ROLL] = 40;
    332 		angles[PITCH] = -15;
    333 		angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
    334 		origin[2] += cg.predictedPlayerState.viewheight;
    335 		return;
    336 	}
    337 
    338 	// add angles based on weapon kick
    339 	VectorAdd (angles, cg.kick_angles, angles);
    340 
    341 	// add angles based on damage kick
    342 	if ( cg.damageTime ) {
    343 		ratio = cg.time - cg.damageTime;
    344 		if ( ratio < DAMAGE_DEFLECT_TIME ) {
    345 			ratio /= DAMAGE_DEFLECT_TIME;
    346 			angles[PITCH] += ratio * cg.v_dmg_pitch;
    347 			angles[ROLL] += ratio * cg.v_dmg_roll;
    348 		} else {
    349 			ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
    350 			if ( ratio > 0 ) {
    351 				angles[PITCH] += ratio * cg.v_dmg_pitch;
    352 				angles[ROLL] += ratio * cg.v_dmg_roll;
    353 			}
    354 		}
    355 	}
    356 
    357 	// add pitch based on fall kick
    358 #if 0
    359 	ratio = ( cg.time - cg.landTime) / FALL_TIME;
    360 	if (ratio < 0)
    361 		ratio = 0;
    362 	angles[PITCH] += ratio * cg.fall_value;
    363 #endif
    364 
    365 	// add angles based on velocity
    366 	VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );
    367 
    368 	delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
    369 	angles[PITCH] += delta * cg_runpitch.value;
    370 	
    371 	delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
    372 	angles[ROLL] -= delta * cg_runroll.value;
    373 
    374 	// add angles based on bob
    375 
    376 	// make sure the bob is visible even at low speeds
    377 	speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
    378 
    379 	delta = cg.bobfracsin * cg_bobpitch.value * speed;
    380 	if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
    381 		delta *= 3;		// crouching
    382 	angles[PITCH] += delta;
    383 	delta = cg.bobfracsin * cg_bobroll.value * speed;
    384 	if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
    385 		delta *= 3;		// crouching accentuates roll
    386 	if (cg.bobcycle & 1)
    387 		delta = -delta;
    388 	angles[ROLL] += delta;
    389 
    390 //===================================
    391 
    392 	// add view height
    393 	origin[2] += cg.predictedPlayerState.viewheight;
    394 
    395 	// smooth out duck height changes
    396 	timeDelta = cg.time - cg.duckTime;
    397 	if ( timeDelta < DUCK_TIME) {
    398 		cg.refdef.vieworg[2] -= cg.duckChange 
    399 			* (DUCK_TIME - timeDelta) / DUCK_TIME;
    400 	}
    401 
    402 	// add bob height
    403 	bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
    404 	if (bob > 6) {
    405 		bob = 6;
    406 	}
    407 
    408 	origin[2] += bob;
    409 
    410 
    411 	// add fall height
    412 	delta = cg.time - cg.landTime;
    413 	if ( delta < LAND_DEFLECT_TIME ) {
    414 		f = delta / LAND_DEFLECT_TIME;
    415 		cg.refdef.vieworg[2] += cg.landChange * f;
    416 	} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
    417 		delta -= LAND_DEFLECT_TIME;
    418 		f = 1.0 - ( delta / LAND_RETURN_TIME );
    419 		cg.refdef.vieworg[2] += cg.landChange * f;
    420 	}
    421 
    422 	// add step offset
    423 	CG_StepOffset();
    424 
    425 	// add kick offset
    426 
    427 	VectorAdd (origin, cg.kick_origin, origin);
    428 
    429 	// pivot the eye based on a neck length
    430 #if 0
    431 	{
    432 #define	NECK_LENGTH		8
    433 	vec3_t			forward, up;
    434  
    435 	cg.refdef.vieworg[2] -= NECK_LENGTH;
    436 	AngleVectors( cg.refdefViewAngles, forward, NULL, up );
    437 	VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
    438 	VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
    439 	}
    440 #endif
    441 }
    442 
    443 //======================================================================
    444 
    445 void CG_ZoomDown_f( void ) { 
    446 	if ( cg.zoomed ) {
    447 		return;
    448 	}
    449 	cg.zoomed = qtrue;
    450 	cg.zoomTime = cg.time;
    451 }
    452 
    453 void CG_ZoomUp_f( void ) { 
    454 	if ( !cg.zoomed ) {
    455 		return;
    456 	}
    457 	cg.zoomed = qfalse;
    458 	cg.zoomTime = cg.time;
    459 }
    460 
    461 
    462 /*
    463 ====================
    464 CG_CalcFov
    465 
    466 Fixed fov at intermissions, otherwise account for fov variable and zooms.
    467 ====================
    468 */
    469 #define	WAVE_AMPLITUDE	1
    470 #define	WAVE_FREQUENCY	0.4
    471 
    472 static int CG_CalcFov( void ) {
    473 	float	x;
    474 	float	phase;
    475 	float	v;
    476 	int		contents;
    477 	float	fov_x, fov_y;
    478 	float	zoomFov;
    479 	float	f;
    480 	int		inwater;
    481 
    482 	if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
    483 		// if in intermission, use a fixed value
    484 		fov_x = 90;
    485 	} else {
    486 		// user selectable
    487 		if ( cgs.dmflags & DF_FIXED_FOV ) {
    488 			// dmflag to prevent wide fov for all clients
    489 			fov_x = 90;
    490 		} else {
    491 			fov_x = cg_fov.value;
    492 			if ( fov_x < 1 ) {
    493 				fov_x = 1;
    494 			} else if ( fov_x > 160 ) {
    495 				fov_x = 160;
    496 			}
    497 		}
    498 
    499 		// account for zooms
    500 		zoomFov = cg_zoomFov.value;
    501 		if ( zoomFov < 1 ) {
    502 			zoomFov = 1;
    503 		} else if ( zoomFov > 160 ) {
    504 			zoomFov = 160;
    505 		}
    506 
    507 		if ( cg.zoomed ) {
    508 			f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
    509 			if ( f > 1.0 ) {
    510 				fov_x = zoomFov;
    511 			} else {
    512 				fov_x = fov_x + f * ( zoomFov - fov_x );
    513 			}
    514 		} else {
    515 			f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
    516 			if ( f > 1.0 ) {
    517 				fov_x = fov_x;
    518 			} else {
    519 				fov_x = zoomFov + f * ( fov_x - zoomFov );
    520 			}
    521 		}
    522 	}
    523 
    524 	x = cg.refdef.width / tan( fov_x / 360 * M_PI );
    525 	fov_y = atan2( cg.refdef.height, x );
    526 	fov_y = fov_y * 360 / M_PI;
    527 
    528 	// warp if underwater
    529 	contents = CG_PointContents( cg.refdef.vieworg, -1 );
    530 	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
    531 		phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
    532 		v = WAVE_AMPLITUDE * sin( phase );
    533 		fov_x += v;
    534 		fov_y -= v;
    535 		inwater = qtrue;
    536 	}
    537 	else {
    538 		inwater = qfalse;
    539 	}
    540 
    541 
    542 	// set it
    543 	cg.refdef.fov_x = fov_x;
    544 	cg.refdef.fov_y = fov_y;
    545 
    546 	if ( !cg.zoomed ) {
    547 		cg.zoomSensitivity = 1;
    548 	} else {
    549 		cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
    550 	}
    551 
    552 	return inwater;
    553 }
    554 
    555 
    556 
    557 /*
    558 ===============
    559 CG_DamageBlendBlob
    560 
    561 ===============
    562 */
    563 static void CG_DamageBlendBlob( void ) {
    564 	int			t;
    565 	int			maxTime;
    566 	refEntity_t		ent;
    567 
    568 	if ( !cg.damageValue ) {
    569 		return;
    570 	}
    571 
    572 	//if (cg.cameraMode) {
    573 	//	return;
    574 	//}
    575 
    576 	// ragePro systems can't fade blends, so don't obscure the screen
    577 	if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
    578 		return;
    579 	}
    580 
    581 	maxTime = DAMAGE_TIME;
    582 	t = cg.time - cg.damageTime;
    583 	if ( t <= 0 || t >= maxTime ) {
    584 		return;
    585 	}
    586 
    587 
    588 	memset( &ent, 0, sizeof( ent ) );
    589 	ent.reType = RT_SPRITE;
    590 	ent.renderfx = RF_FIRST_PERSON;
    591 
    592 	VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
    593 	VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
    594 	VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
    595 
    596 	ent.radius = cg.damageValue * 3;
    597 	ent.customShader = cgs.media.viewBloodShader;
    598 	ent.shaderRGBA[0] = 255;
    599 	ent.shaderRGBA[1] = 255;
    600 	ent.shaderRGBA[2] = 255;
    601 	ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) );
    602 	trap_R_AddRefEntityToScene( &ent );
    603 }
    604 
    605 
    606 /*
    607 ===============
    608 CG_CalcViewValues
    609 
    610 Sets cg.refdef view values
    611 ===============
    612 */
    613 static int CG_CalcViewValues( void ) {
    614 	playerState_t	*ps;
    615 
    616 	memset( &cg.refdef, 0, sizeof( cg.refdef ) );
    617 
    618 	// strings for in game rendering
    619 	// Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) );
    620 	// Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) );
    621 
    622 	// calculate size of 3D view
    623 	CG_CalcVrect();
    624 
    625 	ps = &cg.predictedPlayerState;
    626 /*
    627 	if (cg.cameraMode) {
    628 		vec3_t origin, angles;
    629 		if (trap_getCameraInfo(cg.time, &origin, &angles)) {
    630 			VectorCopy(origin, cg.refdef.vieworg);
    631 			angles[ROLL] = 0;
    632 			VectorCopy(angles, cg.refdefViewAngles);
    633 			AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
    634 			return CG_CalcFov();
    635 		} else {
    636 			cg.cameraMode = qfalse;
    637 		}
    638 	}
    639 */
    640 	// intermission view
    641 	if ( ps->pm_type == PM_INTERMISSION ) {
    642 		VectorCopy( ps->origin, cg.refdef.vieworg );
    643 		VectorCopy( ps->viewangles, cg.refdefViewAngles );
    644 		AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
    645 		return CG_CalcFov();
    646 	}
    647 
    648 	cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
    649 	cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
    650 	cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
    651 		ps->velocity[1] * ps->velocity[1] );
    652 
    653 
    654 	VectorCopy( ps->origin, cg.refdef.vieworg );
    655 	VectorCopy( ps->viewangles, cg.refdefViewAngles );
    656 
    657 	if (cg_cameraOrbit.integer) {
    658 		if (cg.time > cg.nextOrbitTime) {
    659 			cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer;
    660 			cg_thirdPersonAngle.value += cg_cameraOrbit.value;
    661 		}
    662 	}
    663 	// add error decay
    664 	if ( cg_errorDecay.value > 0 ) {
    665 		int		t;
    666 		float	f;
    667 
    668 		t = cg.time - cg.predictedErrorTime;
    669 		f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
    670 		if ( f > 0 && f < 1 ) {
    671 			VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
    672 		} else {
    673 			cg.predictedErrorTime = 0;
    674 		}
    675 	}
    676 
    677 	if ( cg.renderingThirdPerson ) {
    678 		// back away from character
    679 		CG_OffsetThirdPersonView();
    680 	} else {
    681 		// offset for local bobbing and kicks
    682 		CG_OffsetFirstPersonView();
    683 	}
    684 
    685 	// position eye reletive to origin
    686 	AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
    687 
    688 	if ( cg.hyperspace ) {
    689 		cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
    690 	}
    691 
    692 	// field of view
    693 	return CG_CalcFov();
    694 }
    695 
    696 
    697 /*
    698 =====================
    699 CG_PowerupTimerSounds
    700 =====================
    701 */
    702 static void CG_PowerupTimerSounds( void ) {
    703 	int		i;
    704 	int		t;
    705 
    706 	// powerup timers going away
    707 	for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
    708 		t = cg.snap->ps.powerups[i];
    709 		if ( t <= cg.time ) {
    710 			continue;
    711 		}
    712 		if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
    713 			continue;
    714 		}
    715 		if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) {
    716 			trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound );
    717 		}
    718 	}
    719 }
    720 
    721 /*
    722 =====================
    723 CG_AddBufferedSound
    724 =====================
    725 */
    726 void CG_AddBufferedSound( sfxHandle_t sfx ) {
    727 	if ( !sfx )
    728 		return;
    729 	cg.soundBuffer[cg.soundBufferIn] = sfx;
    730 	cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER;
    731 	if (cg.soundBufferIn == cg.soundBufferOut) {
    732 		cg.soundBufferOut++;
    733 	}
    734 }
    735 
    736 /*
    737 =====================
    738 CG_PlayBufferedSounds
    739 =====================
    740 */
    741 static void CG_PlayBufferedSounds( void ) {
    742 	if ( cg.soundTime < cg.time ) {
    743 		if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) {
    744 			trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER);
    745 			cg.soundBuffer[cg.soundBufferOut] = 0;
    746 			cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER;
    747 			cg.soundTime = cg.time + 750;
    748 		}
    749 	}
    750 }
    751 
    752 //=========================================================================
    753 
    754 /*
    755 =================
    756 CG_DrawActiveFrame
    757 
    758 Generates and draws a game scene and status information at the given time.
    759 =================
    760 */
    761 void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) {
    762 	int		inwater;
    763 
    764 	cg.time = serverTime;
    765 	cg.demoPlayback = demoPlayback;
    766 
    767 	// update cvars
    768 	CG_UpdateCvars();
    769 
    770 	// if we are only updating the screen as a loading
    771 	// pacifier, don't even try to read snapshots
    772 	if ( cg.infoScreenText[0] != 0 ) {
    773 		CG_DrawInformation();
    774 		return;
    775 	}
    776 
    777 	// any looped sounds will be respecified as entities
    778 	// are added to the render list
    779 	trap_S_ClearLoopingSounds(qfalse);
    780 
    781 	// clear all the render lists
    782 	trap_R_ClearScene();
    783 
    784 	// set up cg.snap and possibly cg.nextSnap
    785 	CG_ProcessSnapshots();
    786 
    787 	// if we haven't received any snapshots yet, all
    788 	// we can draw is the information screen
    789 	if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
    790 		CG_DrawInformation();
    791 		return;
    792 	}
    793 
    794 	// let the client system know what our weapon and zoom settings are
    795 	trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity );
    796 
    797 	// this counter will be bumped for every valid scene we generate
    798 	cg.clientFrame++;
    799 
    800 	// update cg.predictedPlayerState
    801 	CG_PredictPlayerState();
    802 
    803 	// decide on third person view
    804 	cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
    805 
    806 	// build cg.refdef
    807 	inwater = CG_CalcViewValues();
    808 
    809 	// first person blend blobs, done after AnglesToAxis
    810 	if ( !cg.renderingThirdPerson ) {
    811 		CG_DamageBlendBlob();
    812 	}
    813 
    814 	// build the render lists
    815 	if ( !cg.hyperspace ) {
    816 		CG_AddPacketEntities();			// adter calcViewValues, so predicted player state is correct
    817 		CG_AddMarks();
    818 		CG_AddParticles ();
    819 		CG_AddLocalEntities();
    820 	}
    821 	CG_AddViewWeapon( &cg.predictedPlayerState );
    822 
    823 	// add buffered sounds
    824 	CG_PlayBufferedSounds();
    825 
    826 	// play buffered voice chats
    827 	CG_PlayBufferedVoiceChats();
    828 
    829 	// finish up the rest of the refdef
    830 	if ( cg.testModelEntity.hModel ) {
    831 		CG_AddTestModel();
    832 	}
    833 	cg.refdef.time = cg.time;
    834 	memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
    835 
    836 	// warning sounds when powerup is wearing off
    837 	CG_PowerupTimerSounds();
    838 
    839 	// update audio positions
    840 	trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
    841 
    842 	// make sure the lagometerSample and frame timing isn't done twice when in stereo
    843 	if ( stereoView != STEREO_RIGHT ) {
    844 		cg.frametime = cg.time - cg.oldTime;
    845 		if ( cg.frametime < 0 ) {
    846 			cg.frametime = 0;
    847 		}
    848 		cg.oldTime = cg.time;
    849 		CG_AddLagometerFrameInfo();
    850 	}
    851 	if (cg_timescale.value != cg_timescaleFadeEnd.value) {
    852 		if (cg_timescale.value < cg_timescaleFadeEnd.value) {
    853 			cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
    854 			if (cg_timescale.value > cg_timescaleFadeEnd.value)
    855 				cg_timescale.value = cg_timescaleFadeEnd.value;
    856 		}
    857 		else {
    858 			cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000;
    859 			if (cg_timescale.value < cg_timescaleFadeEnd.value)
    860 				cg_timescale.value = cg_timescaleFadeEnd.value;
    861 		}
    862 		if (cg_timescaleFadeSpeed.value) {
    863 			trap_Cvar_Set("timescale", va("%f", cg_timescale.value));
    864 		}
    865 	}
    866 
    867 	// actually issue the rendering calls
    868 	CG_DrawActive( stereoView );
    869 
    870 	if ( cg_stats.integer ) {
    871 		CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
    872 	}
    873 
    874 
    875 }
    876