1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 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
27 The view is allowed to move slightly from it's true position for bobbing,
28 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
29 entities sent from the server may not include everything in the pvs, especially
30 when crossing a water boudnary.
31
32 */
33
34 cvar_t lcd_x = CVAR2("lcd_x", "0"); // FIXME: make this work sometime...
35
36 cvar_t cl_rollspeed = CVAR2("cl_rollspeed", "200");
37 cvar_t cl_rollangle = CVAR2("cl_rollangle", "2.0");
38
39 cvar_t cl_bob = CVAR3("cl_bob","0.02", false);
40 cvar_t cl_bobcycle = CVAR3("cl_bobcycle","0.6", false);
41 cvar_t cl_bobup = CVAR3("cl_bobup","0.5", false);
42
43 cvar_t v_kicktime = CVAR3("v_kicktime", "0.5", false);
44 cvar_t v_kickroll = CVAR3("v_kickroll", "0.6", false);
45 cvar_t v_kickpitch = CVAR3("v_kickpitch", "0.6", false);
46
47 cvar_t v_iyaw_cycle = CVAR3("v_iyaw_cycle", "2", false);
48 cvar_t v_iroll_cycle = CVAR3("v_iroll_cycle", "0.5", false);
49 cvar_t v_ipitch_cycle = CVAR3("v_ipitch_cycle", "1", false);
50 cvar_t v_iyaw_level = CVAR3("v_iyaw_level", "0.3", false);
51 cvar_t v_iroll_level = CVAR3("v_iroll_level", "0.1", false);
52 cvar_t v_ipitch_level = CVAR3("v_ipitch_level", "0.3", false);
53
54 cvar_t v_idlescale = CVAR3("v_idlescale", "0", false);
55
56 cvar_t crosshair = CVAR3("crosshair", "0", true);
57 cvar_t crosshaircolor = CVAR3("crosshaircolor", "79", true);
58
59 cvar_t cl_crossx = CVAR3("cl_crossx", "0", true);
60 cvar_t cl_crossy = CVAR3("cl_crossy", "0", true);
61
62 #ifdef GLQUAKE
63 cvar_t gl_cshiftpercent = CVAR3("gl_cshiftpercent", "100", false);
64 #endif
65
66 cvar_t v_contentblend = CVAR3("v_contentblend", "1", false);
67
68 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
69
70 extern int in_forward, in_forward2, in_back;
71
72 frame_t *view_frame;
73 player_state_t *view_message;
74
75 /*
76 ===============
77 V_CalcRoll
78
79 ===============
80 */
V_CalcRoll(vec3_t angles,vec3_t velocity)81 float 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 ===============
107 V_CalcBob
108
109 ===============
110 */
V_CalcBob(void)111 float 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
148 cvar_t v_centermove = CVAR3("v_centermove", "0.15", false);
149 cvar_t v_centerspeed = CVAR2("v_centerspeed","500");
150
151
V_StartPitchDrift(void)152 void 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
V_StopPitchDrift(void)168 void V_StopPitchDrift (void)
169 {
170 cl.laststop = cl.time;
171 cl.nodrift = true;
172 cl.pitchvel = 0;
173 }
174
175 /*
176 ===============
177 V_DriftPitch
178
179 Moves the client pitch angle towards cl.idealpitch sent by the server.
180
181 If the user is adjusting pitch manually, either with lookup/lookdown,
182 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
183
184 Drifting is enabled when the center view key is hit, mlook is released and
185 lookspring is non 0, or when
186 ===============
187 */
V_DriftPitch(void)188 void 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
260 cshift_t cshift_empty = { {130,80,50}, 0 };
261 cshift_t cshift_water = { {130,80,50}, 128 };
262 cshift_t cshift_slime = { {0,25,5}, 150 };
263 cshift_t cshift_lava = { {255,80,0}, 150 };
264
265 cvar_t v_gamma = CVAR3("gamma", "1", true);
266
267 byte gammatable[256]; // palette is sent through this
268
269
270 #ifdef GLQUAKE
271 byte ramps[3][256];
272 float v_blend[4]; // rgba 0.0 - 1.0
273 #endif // GLQUAKE
274
BuildGammaTable(float g)275 void 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 =================
299 V_CheckGamma
300 =================
301 */
V_CheckGamma(void)302 qboolean 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 ===============
320 V_ParseDamage
321 ===============
322 */
V_ParseDamage(void)323 void 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 ==================
388 V_cshift_f
389 ==================
390 */
V_cshift_f(void)391 void 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 ==================
402 V_BonusFlash_f
403
404 When you run over an item, the server sends this command
405 ==================
406 */
V_BonusFlash_f(void)407 void 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 =============
417 V_SetContentsColor
418
419 Underwater, lava, etc each has a color shift
420 =============
421 */
V_SetContentsColor(int contents)422 void 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 =============
448 V_CalcPowerupCshift
449 =============
450 */
V_CalcPowerupCshift(void)451 void 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 =============
488 V_CalcBlend
489 =============
490 */
491 #ifdef GLQUAKE
V_CalcBlend(void)492 void 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 =============
533 V_UpdatePalette
534 =============
535 */
536 #ifdef GLQUAKE
V_UpdatePalette(void)537 void 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 =============
628 V_UpdatePalette
629 =============
630 */
V_UpdatePalette(void)631 void 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
angledelta(float a)709 float angledelta (float a)
710 {
711 a = anglemod(a);
712 if (a > 180)
713 a -= 360;
714 return a;
715 }
716
717 /*
718 ==================
719 CalcGunAngle
720 ==================
721 */
CalcGunAngle(void)722 void 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 ==============
773 V_BoundOffsets
774 ==============
775 */
V_BoundOffsets(void)776 void 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 ==============
797 V_AddIdle
798
799 Idle swaying
800 ==============
801 */
V_AddIdle(void)802 void 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 ==============
816 V_CalcViewRoll
817
818 Roll is induced by movement and damage
819 ==============
820 */
V_CalcViewRoll(void)821 void 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 ==================
840 V_CalcIntermissionRefdef
841
842 ==================
843 */
V_CalcIntermissionRefdef(void)844 void 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 ==================
865 V_CalcRefdef
866
867 ==================
868 */
V_CalcRefdef(void)869 void 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 =============
972 DropPunchAngle
973 =============
974 */
DropPunchAngle(void)975 void DropPunchAngle (void)
976 {
977 cl.punchangle -= 10*host_frametime;
978 if (cl.punchangle < 0)
979 cl.punchangle = 0;
980 }
981
982 /*
983 ==================
984 V_RenderView
985
986 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
987 the entity origin, so any view position inside that will be valid
988 ==================
989 */
990 extern vrect_t scr_vrect;
991
V_RenderView(void)992 void V_RenderView (void)
993 {
994 // if (cl.simangles[ROLL])
995 // Sys_Error ("cl.simangles[ROLL]"); // DEBUG
996 cl.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 =============
1028 V_Init
1029 =============
1030 */
V_Init(void)1031 void 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