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