]> icculus.org git repositories - divverent/darkplaces.git/blob - view.c
lighting improvement in rgba mode, may crash some windows drivers
[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 void V_CalcRefdef (void)
710 {
711         entity_t        *ent, *view;
712         int                     i;
713         vec3_t          forward, right, up;
714         vec3_t          angles;
715         float           bob;
716         static float oldz = 0;
717
718         V_DriftPitch ();
719
720 // ent is the player model (visible when out of body)
721         ent = &cl_entities[cl.viewentity];
722 // view is the weapon model (only visible from inside body)
723         view = &cl.viewent;
724         
725
726 // transform the view offset by the model's matrix to get the offset from
727 // model origin for the view
728         ent->angles[YAW] = cl.viewangles[YAW];  // the model should face the view dir
729         ent->angles[PITCH] = -cl.viewangles[PITCH];     // the model should face the view dir
730                                                                                 
731         
732         bob = V_CalcBob ();
733         
734 // refresh position
735         VectorCopy (ent->origin, r_refdef.vieworg);
736         r_refdef.vieworg[2] += cl.viewheight + bob;
737
738 // never let it sit exactly on a node line, because a water plane can
739 // dissapear when viewed with the eye exactly on it.
740 // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
741         r_refdef.vieworg[0] += 1.0/32;
742         r_refdef.vieworg[1] += 1.0/32;
743         r_refdef.vieworg[2] += 1.0/32;
744
745         VectorCopy (cl.viewangles, r_refdef.viewangles);
746         V_CalcViewRoll ();
747         V_AddIdle ();
748
749 // offsets
750         angles[PITCH] = -ent->angles[PITCH];    // because entity pitches are
751                                                                                         //  actually backward
752         angles[YAW] = ent->angles[YAW];
753         angles[ROLL] = ent->angles[ROLL];
754
755         AngleVectors (angles, forward, right, up);
756
757         V_BoundOffsets ();
758                 
759 // set up gun position
760         VectorCopy (cl.viewangles, view->angles);
761         
762         CalcGunAngle ();
763
764         VectorCopy (ent->origin, view->origin);
765         view->origin[2] += cl.viewheight;
766
767         for (i=0 ; i<3 ; i++)
768         {
769                 view->origin[i] += forward[i]*bob*0.4;
770 //              view->origin[i] += right[i]*bob*0.4;
771 //              view->origin[i] += up[i]*bob*0.8;
772         }
773         view->origin[2] += bob;
774
775 // fudge position around to keep amount of weapon visible
776 // roughly equal with different FOV
777
778 #if 0
779         if (cl.model_precache[cl.stats[STAT_WEAPON]] && strcmp (cl.model_precache[cl.stats[STAT_WEAPON]]->name,  "progs/v_shot2.mdl"))
780 #endif
781 // LordHavoc: everyone hates the gun moving around
782 /*
783         if (scr_viewsize.value == 110)
784                 view->origin[2] += 1;
785         else if (scr_viewsize.value == 100)
786                 view->origin[2] += 2;
787         else if (scr_viewsize.value == 90)
788                 view->origin[2] += 1;
789         else if (scr_viewsize.value == 80)
790                 view->origin[2] += 0.5;
791 */
792
793         view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
794         view->frame = cl.stats[STAT_WEAPONFRAME];
795         view->colormap = 0; //vid.colormap;
796
797 // set up the refresh position
798         VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
799
800 // smooth out stair step ups
801 if (cl.onground && ent->origin[2] - oldz > 0)
802 {
803         float steptime;
804         
805         steptime = cl.time - cl.oldtime;
806         if (steptime < 0)
807 //FIXME         I_Error ("steptime < 0");
808                 steptime = 0;
809
810         oldz += steptime * 80;
811         if (oldz > ent->origin[2])
812                 oldz = ent->origin[2];
813         if (ent->origin[2] - oldz > 12)
814                 oldz = ent->origin[2] - 12;
815         r_refdef.vieworg[2] += oldz - ent->origin[2];
816         view->origin[2] += oldz - ent->origin[2];
817 }
818 else
819         oldz = ent->origin[2];
820
821         if (chase_active.value)
822                 Chase_Update ();
823 }
824
825 /*
826 ==================
827 V_RenderView
828
829 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
830 the entity origin, so any view position inside that will be valid
831 ==================
832 */
833 void V_RenderView (void)
834 {
835         if (con_forcedup)
836                 return;
837
838         if (cl.intermission)
839         {       // intermission / finale rendering
840                 V_CalcIntermissionRefdef ();    
841         }
842         else
843         {
844                 if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ )
845                         V_CalcRefdef ();
846         }
847
848         R_PushDlights ();
849
850         R_RenderView ();
851 }
852
853 //============================================================================
854
855 /*
856 =============
857 V_Init
858 =============
859 */
860 void V_Init (void)
861 {
862         Cmd_AddCommand ("v_cshift", V_cshift_f);        
863         Cmd_AddCommand ("bf", V_BonusFlash_f);
864         Cmd_AddCommand ("centerview", V_StartPitchDrift);
865
866         Cvar_RegisterVariable (&v_centermove);
867         Cvar_RegisterVariable (&v_centerspeed);
868
869         Cvar_RegisterVariable (&v_iyaw_cycle);
870         Cvar_RegisterVariable (&v_iroll_cycle);
871         Cvar_RegisterVariable (&v_ipitch_cycle);
872         Cvar_RegisterVariable (&v_iyaw_level);
873         Cvar_RegisterVariable (&v_iroll_level);
874         Cvar_RegisterVariable (&v_ipitch_level);
875
876         Cvar_RegisterVariable (&v_idlescale);
877         Cvar_RegisterVariable (&crosshair);
878         Cvar_RegisterVariable (&cl_crossx);
879         Cvar_RegisterVariable (&cl_crossy);
880         Cvar_RegisterVariable (&gl_cshiftpercent);
881
882         Cvar_RegisterVariable (&cl_rollspeed);
883         Cvar_RegisterVariable (&cl_rollangle);
884         Cvar_RegisterVariable (&cl_bob);
885         Cvar_RegisterVariable (&cl_bobcycle);
886         Cvar_RegisterVariable (&cl_bobup);
887
888         Cvar_RegisterVariable (&v_kicktime);
889         Cvar_RegisterVariable (&v_kickroll);
890         Cvar_RegisterVariable (&v_kickpitch);   
891 }
892
893