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