1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// view.c -- player eye positioning 21 22#include "quakedef.h" 23#include "r_local.h" 24 25/* 26 27The view is allowed to move slightly from it's true position for bobbing, 28but if it exceeds 8 pixels linear distance (spherical, not box), the list of 29entities sent from the server may not include everything in the pvs, especially 30when crossing a water boudnary. 31 32*/ 33 34cvar_t lcd_x = CVAR2("lcd_x", "0"); // FIXME: make this work sometime... 35 36cvar_t cl_rollspeed = CVAR2("cl_rollspeed", "200"); 37cvar_t cl_rollangle = CVAR2("cl_rollangle", "2.0"); 38 39cvar_t cl_bob = CVAR3("cl_bob","0.02", false); 40cvar_t cl_bobcycle = CVAR3("cl_bobcycle","0.6", false); 41cvar_t cl_bobup = CVAR3("cl_bobup","0.5", false); 42 43cvar_t v_kicktime = CVAR3("v_kicktime", "0.5", false); 44cvar_t v_kickroll = CVAR3("v_kickroll", "0.6", false); 45cvar_t v_kickpitch = CVAR3("v_kickpitch", "0.6", false); 46 47cvar_t v_iyaw_cycle = CVAR3("v_iyaw_cycle", "2", false); 48cvar_t v_iroll_cycle = CVAR3("v_iroll_cycle", "0.5", false); 49cvar_t v_ipitch_cycle = CVAR3("v_ipitch_cycle", "1", false); 50cvar_t v_iyaw_level = CVAR3("v_iyaw_level", "0.3", false); 51cvar_t v_iroll_level = CVAR3("v_iroll_level", "0.1", false); 52cvar_t v_ipitch_level = CVAR3("v_ipitch_level", "0.3", false); 53 54cvar_t v_idlescale = CVAR3("v_idlescale", "0", false); 55 56cvar_t crosshair = CVAR3("crosshair", "0", true); 57cvar_t crosshaircolor = CVAR3("crosshaircolor", "79", true); 58 59cvar_t cl_crossx = CVAR3("cl_crossx", "0", true); 60cvar_t cl_crossy = CVAR3("cl_crossy", "0", true); 61 62#ifdef GLQUAKE 63cvar_t gl_cshiftpercent = CVAR3("gl_cshiftpercent", "100", false); 64#endif 65 66cvar_t v_contentblend = CVAR3("v_contentblend", "1", false); 67 68float v_dmg_time, v_dmg_roll, v_dmg_pitch; 69 70extern int in_forward, in_forward2, in_back; 71 72frame_t *view_frame; 73player_state_t *view_message; 74 75/* 76=============== 77V_CalcRoll 78 79=============== 80*/ 81float V_CalcRoll (vec3_t angles, vec3_t velocity) 82{ 83 vec3_t forward, right, up; 84 float sign; 85 float side; 86 float value; 87 88 AngleVectors (angles, forward, right, up); 89 side = DotProduct (velocity, right); 90 sign = side < 0 ? -1 : 1; 91 side = fabs(side); 92 93 value = cl_rollangle.value; 94 95 if (side < cl_rollspeed.value) 96 side = side * value / cl_rollspeed.value; 97 else 98 side = value; 99 100 return side*sign; 101 102} 103 104 105/* 106=============== 107V_CalcBob 108 109=============== 110*/ 111float V_CalcBob (void) 112{ 113 static double bobtime; 114 static float bob; 115 float cycle; 116 117 if (cl.spectator) 118 return 0; 119 120 if (onground == -1) 121 return bob; // just use old value 122 123 bobtime += host_frametime; 124 cycle = bobtime - (int)(bobtime/cl_bobcycle.value)*cl_bobcycle.value; 125 cycle /= cl_bobcycle.value; 126 if (cycle < cl_bobup.value) 127 cycle = M_PI * cycle / cl_bobup.value; 128 else 129 cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); 130 131// bob is proportional to simulated velocity in the xy plane 132// (don't count Z, or jumping messes it up) 133 134 bob = sqrt(cl.simvel[0]*cl.simvel[0] + cl.simvel[1]*cl.simvel[1]) * cl_bob.value; 135 bob = bob*0.3 + bob*0.7*sin(cycle); 136 if (bob > 4) 137 bob = 4; 138 else if (bob < -7) 139 bob = -7; 140 return bob; 141 142} 143 144 145//============================================================================= 146 147 148cvar_t v_centermove = CVAR3("v_centermove", "0.15", false); 149cvar_t v_centerspeed = CVAR2("v_centerspeed","500"); 150 151 152void V_StartPitchDrift (void) 153{ 154#if 1 155 if (cl.laststop == cl.time) 156 { 157 return; // something else is keeping it from drifting 158 } 159#endif 160 if (cl.nodrift || !cl.pitchvel) 161 { 162 cl.pitchvel = v_centerspeed.value; 163 cl.nodrift = false; 164 cl.driftmove = 0; 165 } 166} 167 168void V_StopPitchDrift (void) 169{ 170 cl.laststop = cl.time; 171 cl.nodrift = true; 172 cl.pitchvel = 0; 173} 174 175/* 176=============== 177V_DriftPitch 178 179Moves the client pitch angle towards cl.idealpitch sent by the server. 180 181If the user is adjusting pitch manually, either with lookup/lookdown, 182mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. 183 184Drifting is enabled when the center view key is hit, mlook is released and 185lookspring is non 0, or when 186=============== 187*/ 188void V_DriftPitch (void) 189{ 190 float delta, move; 191 192 if (view_message->onground == -1 || cls.demoplayback ) 193 { 194 cl.driftmove = 0; 195 cl.pitchvel = 0; 196 return; 197 } 198 199// don't count small mouse motion 200 if (cl.nodrift) 201 { 202 if ( fabs(cl.frames[(cls.netchan.outgoing_sequence-1)&UPDATE_MASK].cmd.forwardmove) < 200) 203 cl.driftmove = 0; 204 else 205 cl.driftmove += host_frametime; 206 207 if ( cl.driftmove > v_centermove.value) 208 { 209 V_StartPitchDrift (); 210 } 211 return; 212 } 213 214 delta = 0 - cl.viewangles[PITCH]; 215 216 if (!delta) 217 { 218 cl.pitchvel = 0; 219 return; 220 } 221 222 move = host_frametime * cl.pitchvel; 223 cl.pitchvel += host_frametime * v_centerspeed.value; 224 225//Con_Printf ("move: %f (%f)\n", move, host_frametime); 226 227 if (delta > 0) 228 { 229 if (move > delta) 230 { 231 cl.pitchvel = 0; 232 move = delta; 233 } 234 cl.viewangles[PITCH] += move; 235 } 236 else if (delta < 0) 237 { 238 if (move > -delta) 239 { 240 cl.pitchvel = 0; 241 move = -delta; 242 } 243 cl.viewangles[PITCH] -= move; 244 } 245} 246 247 248 249 250 251/* 252============================================================================== 253 254 PALETTE FLASHES 255 256============================================================================== 257*/ 258 259 260cshift_t cshift_empty = { {130,80,50}, 0 }; 261cshift_t cshift_water = { {130,80,50}, 128 }; 262cshift_t cshift_slime = { {0,25,5}, 150 }; 263cshift_t cshift_lava = { {255,80,0}, 150 }; 264 265cvar_t v_gamma = CVAR3("gamma", "1", true); 266 267byte gammatable[256]; // palette is sent through this 268 269 270#ifdef GLQUAKE 271byte ramps[3][256]; 272float v_blend[4]; // rgba 0.0 - 1.0 273#endif // GLQUAKE 274 275void BuildGammaTable (float g) 276{ 277 int i, inf; 278 279 if (g == 1.0) 280 { 281 for (i=0 ; i<256 ; i++) 282 gammatable[i] = i; 283 return; 284 } 285 286 for (i=0 ; i<256 ; i++) 287 { 288 inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5; 289 if (inf < 0) 290 inf = 0; 291 if (inf > 255) 292 inf = 255; 293 gammatable[i] = inf; 294 } 295} 296 297/* 298================= 299V_CheckGamma 300================= 301*/ 302qboolean V_CheckGamma (void) 303{ 304 static float oldgammavalue; 305 306 if (v_gamma.value == oldgammavalue) 307 return false; 308 oldgammavalue = v_gamma.value; 309 310 BuildGammaTable (v_gamma.value); 311 vid.recalc_refdef = 1; // force a surface cache flush 312 313 return true; 314} 315 316 317 318/* 319=============== 320V_ParseDamage 321=============== 322*/ 323void V_ParseDamage (void) 324{ 325 int armor, blood; 326 vec3_t from; 327 int i; 328 vec3_t forward, right, up; 329 float side; 330 float count; 331 332 armor = MSG_ReadByte (); 333 blood = MSG_ReadByte (); 334 for (i=0 ; i<3 ; i++) 335 from[i] = MSG_ReadCoord (); 336 337 count = blood*0.5 + armor*0.5; 338 if (count < 10) 339 count = 10; 340 341 cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame 342 343 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; 344 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) 345 cl.cshifts[CSHIFT_DAMAGE].percent = 0; 346 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) 347 cl.cshifts[CSHIFT_DAMAGE].percent = 150; 348 349 if (armor > blood) 350 { 351 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; 352 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; 353 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; 354 } 355 else if (armor) 356 { 357 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; 358 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; 359 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; 360 } 361 else 362 { 363 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; 364 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; 365 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; 366 } 367 368// 369// calculate view angle kicks 370// 371 VectorSubtract (from, cl.simorg, from); 372 VectorNormalize (from); 373 374 AngleVectors (cl.simangles, forward, right, up); 375 376 side = DotProduct (from, right); 377 v_dmg_roll = count*side*v_kickroll.value; 378 379 side = DotProduct (from, forward); 380 v_dmg_pitch = count*side*v_kickpitch.value; 381 382 v_dmg_time = v_kicktime.value; 383} 384 385 386/* 387================== 388V_cshift_f 389================== 390*/ 391void V_cshift_f (void) 392{ 393 cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); 394 cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); 395 cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); 396 cshift_empty.percent = atoi(Cmd_Argv(4)); 397} 398 399 400/* 401================== 402V_BonusFlash_f 403 404When you run over an item, the server sends this command 405================== 406*/ 407void V_BonusFlash_f (void) 408{ 409 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; 410 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; 411 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; 412 cl.cshifts[CSHIFT_BONUS].percent = 50; 413} 414 415/* 416============= 417V_SetContentsColor 418 419Underwater, lava, etc each has a color shift 420============= 421*/ 422void V_SetContentsColor (int contents) 423{ 424 if (!v_contentblend.value) { 425 cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; 426 return; 427 } 428 429 switch (contents) 430 { 431 case CONTENTS_EMPTY: 432 cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; 433 break; 434 case CONTENTS_LAVA: 435 cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; 436 break; 437 case CONTENTS_SOLID: 438 case CONTENTS_SLIME: 439 cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; 440 break; 441 default: 442 cl.cshifts[CSHIFT_CONTENTS] = cshift_water; 443 } 444} 445 446/* 447============= 448V_CalcPowerupCshift 449============= 450*/ 451void V_CalcPowerupCshift (void) 452{ 453 if (cl.stats[STAT_ITEMS] & IT_QUAD) 454 { 455 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; 456 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; 457 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; 458 cl.cshifts[CSHIFT_POWERUP].percent = 30; 459 } 460 else if (cl.stats[STAT_ITEMS] & IT_SUIT) 461 { 462 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; 463 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; 464 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; 465 cl.cshifts[CSHIFT_POWERUP].percent = 20; 466 } 467 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) 468 { 469 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; 470 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; 471 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; 472 cl.cshifts[CSHIFT_POWERUP].percent = 100; 473 } 474 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) 475 { 476 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; 477 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; 478 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; 479 cl.cshifts[CSHIFT_POWERUP].percent = 30; 480 } 481 else 482 cl.cshifts[CSHIFT_POWERUP].percent = 0; 483} 484 485 486/* 487============= 488V_CalcBlend 489============= 490*/ 491#ifdef GLQUAKE 492void V_CalcBlend (void) 493{ 494 float r, g, b, a, a2; 495 int j; 496 497 r = 0; 498 g = 0; 499 b = 0; 500 a = 0; 501 502 for (j=0 ; j<NUM_CSHIFTS ; j++) 503 { 504 if (!gl_cshiftpercent.value) 505 continue; 506 507 a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0; 508 509// a2 = (cl.cshifts[j].percent/2)/255.0; 510 if (!a2) 511 continue; 512 a = a + a2*(1-a); 513//Con_Printf ("j:%i a:%f\n", j, a); 514 a2 = a2/a; 515 r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2; 516 g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2; 517 b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2; 518 } 519 520 v_blend[0] = r/255.0; 521 v_blend[1] = g/255.0; 522 v_blend[2] = b/255.0; 523 v_blend[3] = a; 524 if (v_blend[3] > 1) 525 v_blend[3] = 1; 526 if (v_blend[3] < 0) 527 v_blend[3] = 0; 528} 529#endif 530 531/* 532============= 533V_UpdatePalette 534============= 535*/ 536#ifdef GLQUAKE 537void V_UpdatePalette (void) 538{ 539 int i, j; 540 qboolean new; 541 byte *basepal, *newpal; 542 byte pal[768]; 543 float r,g,b,a; 544 int ir, ig, ib; 545 qboolean force; 546 547 V_CalcPowerupCshift (); 548 549 new = false; 550 551 for (i=0 ; i<NUM_CSHIFTS ; i++) 552 { 553 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) 554 { 555 new = true; 556 cl.prev_cshifts[i].percent = cl.cshifts[i].percent; 557 } 558 for (j=0 ; j<3 ; j++) 559 if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) 560 { 561 new = true; 562 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; 563 } 564 } 565 566// drop the damage value 567 cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; 568 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) 569 cl.cshifts[CSHIFT_DAMAGE].percent = 0; 570 571// drop the bonus value 572 cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; 573 if (cl.cshifts[CSHIFT_BONUS].percent <= 0) 574 cl.cshifts[CSHIFT_BONUS].percent = 0; 575 576 force = V_CheckGamma (); 577 if (!new && !force) 578 return; 579 580 V_CalcBlend (); 581 582//Con_Printf("b: %4.2f %4.2f %4.2f %4.6f\n", v_blend[0], v_blend[1], v_blend[2], v_blend[3]); 583 584 a = v_blend[3]; 585 r = 255*v_blend[0]*a; 586 g = 255*v_blend[1]*a; 587 b = 255*v_blend[2]*a; 588 589 a = 1-a; 590 for (i=0 ; i<256 ; i++) 591 { 592 ir = i*a + r; 593 ig = i*a + g; 594 ib = i*a + b; 595 if (ir > 255) 596 ir = 255; 597 if (ig > 255) 598 ig = 255; 599 if (ib > 255) 600 ib = 255; 601 602 ramps[0][i] = gammatable[ir]; 603 ramps[1][i] = gammatable[ig]; 604 ramps[2][i] = gammatable[ib]; 605 } 606 607 basepal = host_basepal; 608 newpal = pal; 609 610 for (i=0 ; i<256 ; i++) 611 { 612 ir = basepal[0]; 613 ig = basepal[1]; 614 ib = basepal[2]; 615 basepal += 3; 616 617 newpal[0] = ramps[0][ir]; 618 newpal[1] = ramps[1][ig]; 619 newpal[2] = ramps[2][ib]; 620 newpal += 3; 621 } 622 623 VID_ShiftPalette (pal); 624} 625#else // !GLQUAKE 626/* 627============= 628V_UpdatePalette 629============= 630*/ 631void V_UpdatePalette (void) 632{ 633 int i, j; 634 qboolean new; 635 byte *basepal, *newpal; 636 byte pal[768]; 637 int r,g,b; 638 qboolean force; 639 640 V_CalcPowerupCshift (); 641 642 new = false; 643 644 for (i=0 ; i<NUM_CSHIFTS ; i++) 645 { 646 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) 647 { 648 new = true; 649 cl.prev_cshifts[i].percent = cl.cshifts[i].percent; 650 } 651 for (j=0 ; j<3 ; j++) 652 if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) 653 { 654 new = true; 655 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; 656 } 657 } 658 659// drop the damage value 660 cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; 661 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) 662 cl.cshifts[CSHIFT_DAMAGE].percent = 0; 663 664// drop the bonus value 665 cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; 666 if (cl.cshifts[CSHIFT_BONUS].percent <= 0) 667 cl.cshifts[CSHIFT_BONUS].percent = 0; 668 669 force = V_CheckGamma (); 670 if (!new && !force) 671 return; 672 673 basepal = host_basepal; 674 newpal = pal; 675 676 for (i=0 ; i<256 ; i++) 677 { 678 r = basepal[0]; 679 g = basepal[1]; 680 b = basepal[2]; 681 basepal += 3; 682 683 for (j=0 ; j<NUM_CSHIFTS ; j++) 684 { 685 r += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[0]-r))>>8; 686 g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8; 687 b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8; 688 } 689 690 newpal[0] = gammatable[r]; 691 newpal[1] = gammatable[g]; 692 newpal[2] = gammatable[b]; 693 newpal += 3; 694 } 695 696 VID_ShiftPalette (pal); 697} 698 699#endif // !GLQUAKE 700 701/* 702============================================================================== 703 704 VIEW RENDERING 705 706============================================================================== 707*/ 708 709float angledelta (float a) 710{ 711 a = anglemod(a); 712 if (a > 180) 713 a -= 360; 714 return a; 715} 716 717/* 718================== 719CalcGunAngle 720================== 721*/ 722void CalcGunAngle (void) 723{ 724 float yaw, pitch, move; 725 static float oldyaw = 0; 726 static float oldpitch = 0; 727 728 yaw = r_refdef.viewangles[YAW]; 729 pitch = -r_refdef.viewangles[PITCH]; 730 731 yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; 732 if (yaw > 10) 733 yaw = 10; 734 if (yaw < -10) 735 yaw = -10; 736 pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; 737 if (pitch > 10) 738 pitch = 10; 739 if (pitch < -10) 740 pitch = -10; 741 move = host_frametime*20; 742 if (yaw > oldyaw) 743 { 744 if (oldyaw + move < yaw) 745 yaw = oldyaw + move; 746 } 747 else 748 { 749 if (oldyaw - move > yaw) 750 yaw = oldyaw - move; 751 } 752 753 if (pitch > oldpitch) 754 { 755 if (oldpitch + move < pitch) 756 pitch = oldpitch + move; 757 } 758 else 759 { 760 if (oldpitch - move > pitch) 761 pitch = oldpitch - move; 762 } 763 764 oldyaw = yaw; 765 oldpitch = pitch; 766 767 cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; 768 cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); 769} 770 771/* 772============== 773V_BoundOffsets 774============== 775*/ 776void V_BoundOffsets (void) 777{ 778// absolutely bound refresh reletive to entity clipping hull 779// so the view can never be inside a solid wall 780 781 if (r_refdef.vieworg[0] < cl.simorg[0] - 14) 782 r_refdef.vieworg[0] = cl.simorg[0] - 14; 783 else if (r_refdef.vieworg[0] > cl.simorg[0] + 14) 784 r_refdef.vieworg[0] = cl.simorg[0] + 14; 785 if (r_refdef.vieworg[1] < cl.simorg[1] - 14) 786 r_refdef.vieworg[1] = cl.simorg[1] - 14; 787 else if (r_refdef.vieworg[1] > cl.simorg[1] + 14) 788 r_refdef.vieworg[1] = cl.simorg[1] + 14; 789 if (r_refdef.vieworg[2] < cl.simorg[2] - 22) 790 r_refdef.vieworg[2] = cl.simorg[2] - 22; 791 else if (r_refdef.vieworg[2] > cl.simorg[2] + 30) 792 r_refdef.vieworg[2] = cl.simorg[2] + 30; 793} 794 795/* 796============== 797V_AddIdle 798 799Idle swaying 800============== 801*/ 802void V_AddIdle (void) 803{ 804 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; 805 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; 806 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; 807 808 cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; 809 cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; 810 cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; 811} 812 813 814/* 815============== 816V_CalcViewRoll 817 818Roll is induced by movement and damage 819============== 820*/ 821void V_CalcViewRoll (void) 822{ 823 float side; 824 825 side = V_CalcRoll (cl.simangles, cl.simvel); 826 r_refdef.viewangles[ROLL] += side; 827 828 if (v_dmg_time > 0) 829 { 830 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; 831 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; 832 v_dmg_time -= host_frametime; 833 } 834 835} 836 837 838/* 839================== 840V_CalcIntermissionRefdef 841 842================== 843*/ 844void V_CalcIntermissionRefdef (void) 845{ 846 entity_t *view; 847 float old; 848 849// view is the weapon model 850 view = &cl.viewent; 851 852 VectorCopy (cl.simorg, r_refdef.vieworg); 853 VectorCopy (cl.simangles, r_refdef.viewangles); 854 view->model = NULL; 855 856// allways idle in intermission 857 old = v_idlescale.value; 858 v_idlescale.value = 1; 859 V_AddIdle (); 860 v_idlescale.value = old; 861} 862 863/* 864================== 865V_CalcRefdef 866 867================== 868*/ 869void V_CalcRefdef (void) 870{ 871 entity_t *view; 872 int i; 873 vec3_t forward, right, up; 874 float bob; 875 static float oldz = 0; 876 877 V_DriftPitch (); 878 879// view is the weapon model (only visible from inside body) 880 view = &cl.viewent; 881 882 bob = V_CalcBob (); 883 884// refresh position from simulated origin 885 VectorCopy (cl.simorg, r_refdef.vieworg); 886 887 r_refdef.vieworg[2] += bob; 888 889// never let it sit exactly on a node line, because a water plane can 890// dissapear when viewed with the eye exactly on it. 891// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis 892 r_refdef.vieworg[0] += 1.0/16; 893 r_refdef.vieworg[1] += 1.0/16; 894 r_refdef.vieworg[2] += 1.0/16; 895 896 VectorCopy (cl.simangles, r_refdef.viewangles); 897 V_CalcViewRoll (); 898 V_AddIdle (); 899 900 if (view_message->flags & PF_GIB) 901 r_refdef.vieworg[2] += 8; // gib view height 902 else if (view_message->flags & PF_DEAD) 903 r_refdef.vieworg[2] -= 16; // corpse view height 904 else 905 r_refdef.vieworg[2] += 22; // view height 906 907 if (view_message->flags & PF_DEAD) // PF_GIB will also set PF_DEAD 908 r_refdef.viewangles[ROLL] = 80; // dead view angle 909 910 911// offsets 912 AngleVectors (cl.simangles, forward, right, up); 913 914// set up gun position 915 VectorCopy (cl.simangles, view->angles); 916 917 CalcGunAngle (); 918 919 VectorCopy (cl.simorg, view->origin); 920 view->origin[2] += 22; 921 922 for (i=0 ; i<3 ; i++) 923 { 924 view->origin[i] += forward[i]*bob*0.4; 925// view->origin[i] += right[i]*bob*0.4; 926// view->origin[i] += up[i]*bob*0.8; 927 } 928 view->origin[2] += bob; 929 930// fudge position around to keep amount of weapon visible 931// roughly equal with different FOV 932 if (scr_viewsize.value == 110) 933 view->origin[2] += 1; 934 else if (scr_viewsize.value == 100) 935 view->origin[2] += 2; 936 else if (scr_viewsize.value == 90) 937 view->origin[2] += 1; 938 else if (scr_viewsize.value == 80) 939 view->origin[2] += 0.5; 940 941 if (view_message->flags & (PF_GIB|PF_DEAD) ) 942 view->model = NULL; 943 else 944 view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; 945 view->frame = view_message->weaponframe; 946 view->colormap = vid.colormap; 947 948// set up the refresh position 949 r_refdef.viewangles[PITCH] += cl.punchangle; 950 951// smooth out stair step ups 952 if ( (view_message->onground != -1) && (cl.simorg[2] - oldz > 0) ) 953 { 954 float steptime; 955 956 steptime = host_frametime; 957 958 oldz += steptime * 80; 959 if (oldz > cl.simorg[2]) 960 oldz = cl.simorg[2]; 961 if (cl.simorg[2] - oldz > 12) 962 oldz = cl.simorg[2] - 12; 963 r_refdef.vieworg[2] += oldz - cl.simorg[2]; 964 view->origin[2] += oldz - cl.simorg[2]; 965 } 966 else 967 oldz = cl.simorg[2]; 968} 969 970/* 971============= 972DropPunchAngle 973============= 974*/ 975void DropPunchAngle (void) 976{ 977 cl.punchangle -= 10*host_frametime; 978 if (cl.punchangle < 0) 979 cl.punchangle = 0; 980} 981 982/* 983================== 984V_RenderView 985 986The player's clipping box goes from (-16 -16 -24) to (16 16 32) from 987the entity origin, so any view position inside that will be valid 988================== 989*/ 990extern vrect_t scr_vrect; 991 992void V_RenderView (void) 993{ 994// if (cl.simangles[ROLL]) 995// Sys_Error ("cl.simangles[ROLL]"); // DEBUG 996cl.simangles[ROLL] = 0; // FIXME @@@ 997 998 if (cls.state != ca_active) 999 return; 1000 1001 view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; 1002 view_message = &view_frame->playerstate[cl.playernum]; 1003 1004 DropPunchAngle (); 1005 if (cl.intermission) 1006 { // intermission / finale rendering 1007 V_CalcIntermissionRefdef (); 1008 } 1009 else 1010 { 1011 V_CalcRefdef (); 1012 } 1013 1014 R_PushDlights (); 1015 R_RenderView (); 1016 1017#ifndef GLQUAKE 1018 if (crosshair.value) 1019 Draw_Crosshair(); 1020#endif 1021 1022} 1023 1024//============================================================================ 1025 1026/* 1027============= 1028V_Init 1029============= 1030*/ 1031void V_Init (void) 1032{ 1033 Cmd_AddCommand ("v_cshift", V_cshift_f); 1034 Cmd_AddCommand ("bf", V_BonusFlash_f); 1035 Cmd_AddCommand ("centerview", V_StartPitchDrift); 1036 1037 Cvar_RegisterVariable (&v_centermove); 1038 Cvar_RegisterVariable (&v_centerspeed); 1039 1040 Cvar_RegisterVariable (&v_iyaw_cycle); 1041 Cvar_RegisterVariable (&v_iroll_cycle); 1042 Cvar_RegisterVariable (&v_ipitch_cycle); 1043 Cvar_RegisterVariable (&v_iyaw_level); 1044 Cvar_RegisterVariable (&v_iroll_level); 1045 Cvar_RegisterVariable (&v_ipitch_level); 1046 1047 Cvar_RegisterVariable (&v_contentblend); 1048 1049 Cvar_RegisterVariable (&v_idlescale); 1050 Cvar_RegisterVariable (&crosshaircolor); 1051 Cvar_RegisterVariable (&crosshair); 1052 Cvar_RegisterVariable (&cl_crossx); 1053 Cvar_RegisterVariable (&cl_crossy); 1054#ifdef GLQUAKE 1055 Cvar_RegisterVariable (&gl_cshiftpercent); 1056#endif 1057 1058 Cvar_RegisterVariable (&cl_rollspeed); 1059 Cvar_RegisterVariable (&cl_rollangle); 1060 Cvar_RegisterVariable (&cl_bob); 1061 Cvar_RegisterVariable (&cl_bobcycle); 1062 Cvar_RegisterVariable (&cl_bobup); 1063 1064 Cvar_RegisterVariable (&v_kicktime); 1065 Cvar_RegisterVariable (&v_kickroll); 1066 Cvar_RegisterVariable (&v_kickpitch); 1067 1068 BuildGammaTable (1.0); // no gamma yet 1069 Cvar_RegisterVariable (&v_gamma); 1070} 1071 1072 1073