rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
[xonotic/darkplaces.git] / view.c
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
24 /*
25
26 The view is allowed to move slightly from it's true position for bobbing,
27 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
28 entities sent from the server may not include everything in the pvs, especially
29 when crossing a water boudnary.
30
31 */
32
33 cvar_t  cl_rollspeed = {0, "cl_rollspeed", "200"};
34 cvar_t  cl_rollangle = {0, "cl_rollangle", "2.0"};
35
36 cvar_t  cl_bob = {0, "cl_bob","0.02"};
37 cvar_t  cl_bobcycle = {0, "cl_bobcycle","0.6"};
38 cvar_t  cl_bobup = {0, "cl_bobup","0.5"};
39
40 cvar_t  v_kicktime = {0, "v_kicktime", "0.5"};
41 cvar_t  v_kickroll = {0, "v_kickroll", "0.6"};
42 cvar_t  v_kickpitch = {0, "v_kickpitch", "0.6"};
43
44 cvar_t  v_punch = {0, "v_punch", "1"};
45
46 cvar_t  v_iyaw_cycle = {0, "v_iyaw_cycle", "2"};
47 cvar_t  v_iroll_cycle = {0, "v_iroll_cycle", "0.5"};
48 cvar_t  v_ipitch_cycle = {0, "v_ipitch_cycle", "1"};
49 cvar_t  v_iyaw_level = {0, "v_iyaw_level", "0.3"};
50 cvar_t  v_iroll_level = {0, "v_iroll_level", "0.1"};
51 cvar_t  v_ipitch_level = {0, "v_ipitch_level", "0.3"};
52
53 cvar_t  v_idlescale = {0, "v_idlescale", "0"};
54
55 cvar_t  crosshair = {CVAR_SAVE, "crosshair", "0"};
56
57 //cvar_t        gl_cshiftpercent = {0, "gl_cshiftpercent", "100"};
58 cvar_t  gl_polyblend = {CVAR_SAVE, "gl_polyblend", "1"};
59
60 cvar_t  v_centermove = {0, "v_centermove", "0.15"};
61 cvar_t  v_centerspeed = {0, "v_centerspeed","500"};
62
63 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
64
65
66 /*
67 ===============
68 V_CalcRoll
69
70 Used by view and sv_user
71 ===============
72 */
73 float V_CalcRoll (vec3_t angles, vec3_t velocity)
74 {
75         vec3_t  right;
76         float   sign;
77         float   side;
78         float   value;
79         
80         AngleVectors (angles, NULL, right, NULL);
81         side = DotProduct (velocity, right);
82         sign = side < 0 ? -1 : 1;
83         side = fabs(side);
84         
85         value = cl_rollangle.value;
86 //      if (cl.inwater)
87 //              value *= 6;
88
89         if (side < cl_rollspeed.value)
90                 side = side * value / cl_rollspeed.value;
91         else
92                 side = value;
93
94         return side*sign;
95
96 }
97
98
99 /*
100 ===============
101 V_CalcBob
102
103 ===============
104 */
105 float V_CalcBob (void)
106 {
107         float   bob;
108         float   cycle;
109         
110         cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value;
111         cycle /= cl_bobcycle.value;
112         if (cycle < cl_bobup.value)
113                 cycle = M_PI * cycle / cl_bobup.value;
114         else
115                 cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
116
117 // bob is proportional to velocity in the xy plane
118 // (don't count Z, or jumping messes it up)
119
120         bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
121 //Con_Printf ("speed: %5.1f\n", Length(cl.velocity));
122         bob = bob*0.3 + bob*0.7*sin(cycle);
123         if (bob > 4)
124                 bob = 4;
125         else if (bob < -7)
126                 bob = -7;
127         return bob;
128         
129 }
130
131
132 //=============================================================================
133
134
135 void V_StartPitchDrift (void)
136 {
137 #if 1
138         if (cl.laststop == cl.time)
139         {
140                 return;         // something else is keeping it from drifting
141         }
142 #endif
143         if (cl.nodrift || !cl.pitchvel)
144         {
145                 cl.pitchvel = v_centerspeed.value;
146                 cl.nodrift = false;
147                 cl.driftmove = 0;
148         }
149 }
150
151 void V_StopPitchDrift (void)
152 {
153         cl.laststop = cl.time;
154         cl.nodrift = true;
155         cl.pitchvel = 0;
156 }
157
158 /*
159 ===============
160 V_DriftPitch
161
162 Moves the client pitch angle towards cl.idealpitch sent by the server.
163
164 If the user is adjusting pitch manually, either with lookup/lookdown,
165 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
166
167 Drifting is enabled when the center view key is hit, mlook is released and
168 lookspring is non 0, or when
169 ===============
170 */
171 void V_DriftPitch (void)
172 {
173         float           delta, move;
174
175         if (noclip_anglehack || !cl.onground || cls.demoplayback )
176         {
177                 cl.driftmove = 0;
178                 cl.pitchvel = 0;
179                 return;
180         }
181
182 // don't count small mouse motion
183         if (cl.nodrift)
184         {
185                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
186                         cl.driftmove = 0;
187                 else
188                         cl.driftmove += cl.frametime;
189         
190                 if ( cl.driftmove > v_centermove.value)
191                 {
192                         V_StartPitchDrift ();
193                 }
194                 return;
195         }
196         
197         delta = cl.idealpitch - cl.viewangles[PITCH];
198
199         if (!delta)
200         {
201                 cl.pitchvel = 0;
202                 return;
203         }
204
205         move = cl.frametime * cl.pitchvel;
206         cl.pitchvel += cl.frametime * v_centerspeed.value;
207         
208 //Con_Printf ("move: %f (%f)\n", move, cl.frametime);
209
210         if (delta > 0)
211         {
212                 if (move > delta)
213                 {
214                         cl.pitchvel = 0;
215                         move = delta;
216                 }
217                 cl.viewangles[PITCH] += move;
218         }
219         else if (delta < 0)
220         {
221                 if (move > -delta)
222                 {
223                         cl.pitchvel = 0;
224                         move = -delta;
225                 }
226                 cl.viewangles[PITCH] -= move;
227         }
228 }
229
230
231
232
233
234 /*
235 ============================================================================== 
236  
237                                                 SCREEN FLASHES 
238  
239 ============================================================================== 
240 */ 
241  
242  
243 cshift_t        cshift_empty = { {130,80,50}, 0 };
244 cshift_t        cshift_water = { {130,80,50}, 128 };
245 cshift_t        cshift_slime = { {0,25,5}, 150 };
246 cshift_t        cshift_lava = { {255,80,0}, 150 };
247
248 byte            ramps[3][256];
249 float           v_blend[4];             // rgba 0.0 - 1.0
250
251 /*
252 ===============
253 V_ParseDamage
254 ===============
255 */
256 void V_ParseDamage (void)
257 {
258         int             armor, blood;
259         vec3_t  from;
260         int             i;
261         vec3_t  forward, right;
262         entity_t        *ent;
263         float   side;
264         float   count;
265         
266         armor = MSG_ReadByte ();
267         blood = MSG_ReadByte ();
268         for (i=0 ; i<3 ; i++)
269                 from[i] = MSG_ReadCoord ();
270
271         count = blood*0.5 + armor*0.5;
272         if (count < 10)
273                 count = 10;
274
275         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
276
277         if (gl_polyblend.value)
278         {
279                 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
280                 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
281                         cl.cshifts[CSHIFT_DAMAGE].percent = 0;
282                 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
283                         cl.cshifts[CSHIFT_DAMAGE].percent = 150;
284
285                 if (armor > blood)              
286                 {
287                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
288                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
289                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
290                 }
291                 else if (armor)
292                 {
293                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
294                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
295                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
296                 }
297                 else
298                 {
299                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
300                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
301                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
302                 }
303         }
304
305 //
306 // calculate view angle kicks
307 //
308         ent = &cl_entities[cl.viewentity];
309         
310         VectorSubtract (from, ent->render.origin, from);
311         VectorNormalize (from);
312         
313         AngleVectors (ent->render.angles, forward, right, NULL);
314
315         side = DotProduct (from, right);
316         v_dmg_roll = count*side*v_kickroll.value;
317         
318         side = DotProduct (from, forward);
319         v_dmg_pitch = count*side*v_kickpitch.value;
320
321         v_dmg_time = v_kicktime.value;
322 }
323
324
325 /*
326 ==================
327 V_cshift_f
328 ==================
329 */
330 void V_cshift_f (void)
331 {
332         cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
333         cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
334         cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
335         cshift_empty.percent = atoi(Cmd_Argv(4));
336 }
337
338
339 /*
340 ==================
341 V_BonusFlash_f
342
343 When you run over an item, the server sends this command
344 ==================
345 */
346 void V_BonusFlash_f (void)
347 {
348         if (gl_polyblend.value)
349         {
350                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
351                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
352                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
353                 cl.cshifts[CSHIFT_BONUS].percent = 50;
354         }
355 }
356
357 /*
358 =============
359 V_SetContentsColor
360
361 Underwater, lava, etc each has a color shift
362 =============
363 */
364 void V_SetContentsColor (int contents)
365 {
366         cshift_t* c;
367         c = &cl.cshifts[CSHIFT_CONTENTS]; // just to shorten the code below
368         if (!gl_polyblend.value)
369         {
370                 c->percent = 0;
371                 return;
372         }
373         switch (contents)
374         {
375         case CONTENTS_EMPTY:
376         case CONTENTS_SOLID:
377                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
378                 c->destcolor[0] = cshift_empty.destcolor[0];
379                 c->destcolor[1] = cshift_empty.destcolor[1];
380                 c->destcolor[2] = cshift_empty.destcolor[2];
381                 c->percent = cshift_empty.percent;
382                 break;
383         case CONTENTS_LAVA:
384                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
385                 c->destcolor[0] = cshift_lava.destcolor[0];
386                 c->destcolor[1] = cshift_lava.destcolor[1];
387                 c->destcolor[2] = cshift_lava.destcolor[2];
388                 c->percent = cshift_lava.percent;
389                 break;
390         case CONTENTS_SLIME:
391                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
392                 c->destcolor[0] = cshift_slime.destcolor[0];
393                 c->destcolor[1] = cshift_slime.destcolor[1];
394                 c->destcolor[2] = cshift_slime.destcolor[2];
395                 c->percent = cshift_slime.percent;
396                 break;
397         default:
398                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
399                 c->destcolor[0] = cshift_water.destcolor[0];
400                 c->destcolor[1] = cshift_water.destcolor[1];
401                 c->destcolor[2] = cshift_water.destcolor[2];
402                 c->percent = cshift_water.percent;
403         }
404 }
405
406 /*
407 =============
408 V_CalcPowerupCshift
409 =============
410 */
411 void V_CalcPowerupCshift (void)
412 {
413         if (!gl_polyblend.value)
414         {
415                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
416                 return;
417         }
418         if (cl.items & IT_QUAD)
419         {
420                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
421                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
422                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
423                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
424         }
425         else if (cl.items & IT_SUIT)
426         {
427                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
428                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
429                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
430                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
431         }
432         else if (cl.items & IT_INVISIBILITY)
433         {
434                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
435                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
436                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
437                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
438         }
439         else if (cl.items & IT_INVULNERABILITY)
440         {
441                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
442                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
443                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
444                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
445         }
446         else
447                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
448 }
449
450 /*
451 =============
452 V_CalcBlend
453 =============
454 */
455 // LordHavoc: fixed V_CalcBlend
456 void V_CalcBlend (void)
457 {
458         float   r, g, b, a, a2;
459         int             j;
460
461         r = 0;
462         g = 0;
463         b = 0;
464         a = 0;
465
466 //      if (gl_cshiftpercent.value)
467 //      {
468                 for (j=0 ; j<NUM_CSHIFTS ; j++) 
469                 {
470 //                      a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
471                         a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
472
473                         if (!a2)
474                                 continue;
475                         if (a2 > 1)
476                                 a2 = 1;
477                         r += (cl.cshifts[j].destcolor[0]-r) * a2;
478                         g += (cl.cshifts[j].destcolor[1]-g) * a2;
479                         b += (cl.cshifts[j].destcolor[2]-b) * a2;
480                         a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
481                 }
482                 // saturate color (to avoid blending in black)
483                 if (a)
484                 {
485                         a2 = 1 / a;
486                         r *= a2;
487                         g *= a2;
488                         b *= a2;
489                 }
490 //      }
491
492         v_blend[0] = bound(0, r * (1.0/255.0), 1);
493         v_blend[1] = bound(0, g * (1.0/255.0), 1);
494         v_blend[2] = bound(0, b * (1.0/255.0), 1);
495         v_blend[3] = bound(0, a              , 1);
496 }
497
498 /*
499 =============
500 V_UpdateBlends
501 =============
502 */
503 void V_UpdateBlends (void)
504 {
505         int             i, j;
506         qboolean        new;
507
508         V_CalcPowerupCshift ();
509         
510         new = false;
511
512         for (i=0 ; i<NUM_CSHIFTS ; i++)
513         {
514                 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
515                 {
516                         new = true;
517                         cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
518                 }
519                 for (j=0 ; j<3 ; j++)
520                         if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
521                         {
522                                 new = true;
523                                 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
524                         }
525         }
526         
527 // drop the damage value
528         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
529         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
530                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
531
532 // drop the bonus value
533         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
534         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
535                 cl.cshifts[CSHIFT_BONUS].percent = 0;
536
537         if (!new)
538                 return;
539
540         V_CalcBlend ();
541 }
542
543 /* 
544 ============================================================================== 
545  
546                                                 VIEW RENDERING 
547  
548 ============================================================================== 
549 */ 
550
551 float angledelta (float a)
552 {
553         a = ANGLEMOD(a);
554         if (a > 180)
555                 a -= 360;
556         return a;
557 }
558
559 /*
560 ==================
561 CalcGunAngle
562 ==================
563 */
564 void CalcGunAngle (void)
565 {
566         cl.viewent.render.angles[YAW] = r_refdef.viewangles[YAW];
567         cl.viewent.render.angles[PITCH] = -r_refdef.viewangles[PITCH];
568
569         cl.viewent.render.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
570         cl.viewent.render.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
571         cl.viewent.render.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
572 }
573
574 /*
575 ==============
576 V_BoundOffsets
577 ==============
578 */
579 void V_BoundOffsets (void)
580 {
581         entity_t        *ent;
582         
583         ent = &cl_entities[cl.viewentity];
584
585 // absolutely bound refresh relative to entity clipping hull
586 // so the view can never be inside a solid wall
587
588         if (r_refdef.vieworg[0] < ent->render.origin[0] - 14)
589                 r_refdef.vieworg[0] = ent->render.origin[0] - 14;
590         else if (r_refdef.vieworg[0] > ent->render.origin[0] + 14)
591                 r_refdef.vieworg[0] = ent->render.origin[0] + 14;
592         if (r_refdef.vieworg[1] < ent->render.origin[1] - 14)
593                 r_refdef.vieworg[1] = ent->render.origin[1] - 14;
594         else if (r_refdef.vieworg[1] > ent->render.origin[1] + 14)
595                 r_refdef.vieworg[1] = ent->render.origin[1] + 14;
596         if (r_refdef.vieworg[2] < ent->render.origin[2] - 22)
597                 r_refdef.vieworg[2] = ent->render.origin[2] - 22;
598         else if (r_refdef.vieworg[2] > ent->render.origin[2] + 30)
599                 r_refdef.vieworg[2] = ent->render.origin[2] + 30;
600 }
601
602 /*
603 ==============
604 V_AddIdle
605
606 Idle swaying
607 ==============
608 */
609 void V_AddIdle (void)
610 {
611         r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
612         r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
613         r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
614 }
615
616
617 /*
618 ==============
619 V_CalcViewRoll
620
621 Roll is induced by movement and damage
622 ==============
623 */
624 void V_CalcViewRoll (void)
625 {
626         float           side;
627                 
628         side = V_CalcRoll (cl_entities[cl.viewentity].render.angles, cl.velocity);
629         r_refdef.viewangles[ROLL] += side;
630
631         if (v_dmg_time > 0)
632         {
633                 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
634                 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
635                 v_dmg_time -= cl.frametime;
636         }
637
638         if (cl.stats[STAT_HEALTH] <= 0)
639         {
640                 r_refdef.viewangles[ROLL] = 80; // dead view angle
641                 return;
642         }
643
644 }
645
646
647 /*
648 ==================
649 V_CalcIntermissionRefdef
650
651 ==================
652 */
653 void V_CalcIntermissionRefdef (void)
654 {
655         entity_t        *ent, *view;
656         float           old;
657
658 // ent is the player model (visible when out of body)
659         ent = &cl_entities[cl.viewentity];
660 // view is the weapon model (only visible from inside body)
661         view = &cl.viewent;
662
663         VectorCopy (ent->render.origin, r_refdef.vieworg);
664         VectorCopy (ent->render.angles, r_refdef.viewangles);
665         view->render.model = NULL;
666
667 // always idle in intermission
668         old = v_idlescale.value;
669         v_idlescale.value = 1;
670         V_AddIdle ();
671         v_idlescale.value = old;
672 }
673
674 /*
675 ==================
676 V_CalcRefdef
677
678 ==================
679 */
680 void V_CalcRefdef (void)
681 {
682         entity_t        *ent, *view;
683         int                     i;
684         vec3_t          forward;
685         vec3_t          angles;
686         float           bob;
687 //      static float oldz = 0;
688
689         V_DriftPitch ();
690
691 // ent is the player model (visible when out of body)
692         ent = &cl_entities[cl.viewentity];
693 // view is the weapon model (only visible from inside body)
694         view = &cl.viewent;
695
696
697         if (chase_active.value)
698         {
699                 VectorCopy (ent->render.origin, r_refdef.vieworg);
700                 VectorCopy (cl.viewangles, r_refdef.viewangles);
701                 Chase_Update ();
702                 V_AddIdle ();
703         }
704         else
705         {
706         // transform the view offset by the model's matrix to get the offset from model origin for the view
707         //      if (!chase_active.value) // LordHavoc: get rid of angle problems in chase_active mode
708         //      {
709         //              ent->render.angles[YAW] = cl.viewangles[YAW];   // the model should face the view dir
710         //              ent->render.angles[PITCH] = -cl.viewangles[PITCH];      // the model should face the view dir
711         //      }
712
713                 bob = V_CalcBob ();
714
715         // refresh position
716                 VectorCopy (ent->render.origin, r_refdef.vieworg);
717                 r_refdef.vieworg[2] += cl.viewheight + bob;
718
719                 // LordHavoc: the protocol has changed...  so this is an obsolete approach
720         // never let it sit exactly on a node line, because a water plane can
721         // dissapear when viewed with the eye exactly on it.
722         // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
723         //      r_refdef.vieworg[0] += 1.0/32;
724         //      r_refdef.vieworg[1] += 1.0/32;
725         //      r_refdef.vieworg[2] += 1.0/32;
726
727                 if (!intimerefresh)
728                         VectorCopy (cl.viewangles, r_refdef.viewangles);
729                 V_CalcViewRoll ();
730                 V_AddIdle ();
731
732         // offsets
733                 angles[PITCH] = -ent->render.angles[PITCH];     // because entity pitches are actually backward
734                 angles[YAW] = ent->render.angles[YAW];
735                 angles[ROLL] = ent->render.angles[ROLL];
736
737                 AngleVectors (angles, forward, NULL, NULL);
738
739                 V_BoundOffsets ();
740
741         // set up gun position
742                 VectorCopy (ent->render.origin, view->render.origin);
743                 view->render.origin[2] += cl.viewheight;
744                 VectorCopy (cl.viewangles, view->render.angles);
745
746                 CalcGunAngle ();
747
748                 for (i=0 ; i<3 ; i++)
749                 {
750                         view->render.origin[i] += forward[i]*bob*0.4;
751         //              view->render.origin[i] += right[i]*bob*0.4;
752         //              view->render.origin[i] += up[i]*bob*0.8;
753                 }
754                 view->render.origin[2] += bob;
755
756                 // FIXME: this setup code is somewhat evil (CL_LerpUpdate should be private)
757                 CL_LerpUpdate(view, cl.stats[STAT_WEAPONFRAME], cl.stats[STAT_WEAPON]);
758
759                 view->render.model = cl.model_precache[cl.stats[STAT_WEAPON]];
760                 view->render.frame = cl.stats[STAT_WEAPONFRAME];
761                 view->render.colormap = -1; // no special coloring
762                 view->render.alpha = ent->render.alpha; // LordHavoc: if the player is transparent, so is the gun
763                 view->render.effects = ent->render.effects;
764                 view->render.scale = 1;
765
766         // set up the refresh position
767
768                 // LordHavoc: this never looked all that good to begin with...
769                 /*
770         // smooth out stair step ups
771         if (cl.onground && ent->render.origin[2] - oldz > 0)
772         {
773                 float steptime;
774
775                 steptime = cl.time - cl.oldtime;
776                 if (steptime < 0)
777         //FIXME         I_Error ("steptime < 0");
778                         steptime = 0;
779
780                 oldz += steptime * 80;
781                 if (oldz > ent->render.origin[2])
782                         oldz = ent->render.origin[2];
783                 if (ent->render.origin[2] - oldz > 12)
784                         oldz = ent->render.origin[2] - 12;
785                 r_refdef.vieworg[2] += oldz - ent->render.origin[2];
786                 view->render.origin[2] += oldz - ent->render.origin[2];
787         }
788         else
789                 oldz = ent->render.origin[2];
790                 */
791
792         // LordHavoc: origin view kick added
793                 if (!intimerefresh && v_punch.value)
794                 {
795                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
796                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
797                 }
798         }
799 }
800
801 /*
802 ==================
803 V_RenderView
804
805 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
806 the entity origin, so any view position inside that will be valid
807 ==================
808 */
809 void V_RenderView (void)
810 {
811         if (scr_con_current >= vid.conheight)
812                 return;
813
814         if (cl.intermission)
815                 V_CalcIntermissionRefdef ();    
816         else
817                 V_CalcRefdef ();
818
819         R_RenderView ();
820 }
821
822 //============================================================================
823
824 /*
825 =============
826 V_Init
827 =============
828 */
829 void V_Init (void)
830 {
831         Cmd_AddCommand ("v_cshift", V_cshift_f);        
832         Cmd_AddCommand ("bf", V_BonusFlash_f);
833         Cmd_AddCommand ("centerview", V_StartPitchDrift);
834
835         Cvar_RegisterVariable (&v_centermove);
836         Cvar_RegisterVariable (&v_centerspeed);
837
838         Cvar_RegisterVariable (&v_iyaw_cycle);
839         Cvar_RegisterVariable (&v_iroll_cycle);
840         Cvar_RegisterVariable (&v_ipitch_cycle);
841         Cvar_RegisterVariable (&v_iyaw_level);
842         Cvar_RegisterVariable (&v_iroll_level);
843         Cvar_RegisterVariable (&v_ipitch_level);
844
845         Cvar_RegisterVariable (&v_idlescale);
846         Cvar_RegisterVariable (&crosshair);
847 //      Cvar_RegisterVariable (&gl_cshiftpercent);
848         Cvar_RegisterVariable (&gl_polyblend);
849
850         Cvar_RegisterVariable (&cl_rollspeed);
851         Cvar_RegisterVariable (&cl_rollangle);
852         Cvar_RegisterVariable (&cl_bob);
853         Cvar_RegisterVariable (&cl_bobcycle);
854         Cvar_RegisterVariable (&cl_bobup);
855
856         Cvar_RegisterVariable (&v_kicktime);
857         Cvar_RegisterVariable (&v_kickroll);
858         Cvar_RegisterVariable (&v_kickpitch);   
859
860         Cvar_RegisterVariable (&v_punch);
861 }
862
863