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