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