]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
Forgot to update build number for 105 release
[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 = {"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_punch = {"v_punch", "1", false};
45
46 cvar_t  v_iyaw_cycle = {"v_iyaw_cycle", "2", false};
47 cvar_t  v_iroll_cycle = {"v_iroll_cycle", "0.5", false};
48 cvar_t  v_ipitch_cycle = {"v_ipitch_cycle", "1", false};
49 cvar_t  v_iyaw_level = {"v_iyaw_level", "0.3", false};
50 cvar_t  v_iroll_level = {"v_iroll_level", "0.1", false};
51 cvar_t  v_ipitch_level = {"v_ipitch_level", "0.3", false};
52
53 cvar_t  v_idlescale = {"v_idlescale", "0", false};
54
55 cvar_t  crosshair = {"crosshair", "0", true};
56 cvar_t  cl_crossx = {"cl_crossx", "0", false};
57 cvar_t  cl_crossy = {"cl_crossy", "0", false};
58
59 //cvar_t        gl_cshiftpercent = {"gl_cshiftpercent", "100", false};
60 cvar_t  gl_polyblend = {"gl_polyblend", "1", true};
61
62 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
63
64
65 /*
66 ===============
67 V_CalcRoll
68
69 Used by view and sv_user
70 ===============
71 */
72 float V_CalcRoll (vec3_t angles, vec3_t velocity)
73 {
74         vec3_t  right;
75         float   sign;
76         float   side;
77         float   value;
78         
79         AngleVectors (angles, NULL, right, NULL);
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 += cl.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 = cl.frametime * cl.pitchvel;
209         cl.pitchvel += cl.frametime * v_centerspeed.value;
210         
211 //Con_Printf ("move: %f (%f)\n", move, cl.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                                                 SCREEN 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;
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;                // put sbar face into pain frame
279
280         if (gl_polyblend.value)
281         {
282                 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
283                 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
284                         cl.cshifts[CSHIFT_DAMAGE].percent = 0;
285                 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
286                         cl.cshifts[CSHIFT_DAMAGE].percent = 150;
287
288                 if (armor > blood)              
289                 {
290                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
291                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
292                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
293                 }
294                 else if (armor)
295                 {
296                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
297                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
298                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
299                 }
300                 else
301                 {
302                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
303                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
304                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
305                 }
306         }
307
308 //
309 // calculate view angle kicks
310 //
311         ent = &cl_entities[cl.viewentity];
312         
313         VectorSubtract (from, ent->render.origin, from);
314         VectorNormalize (from);
315         
316         AngleVectors (ent->render.angles, forward, right, NULL);
317
318         side = DotProduct (from, right);
319         v_dmg_roll = count*side*v_kickroll.value;
320         
321         side = DotProduct (from, forward);
322         v_dmg_pitch = count*side*v_kickpitch.value;
323
324         v_dmg_time = v_kicktime.value;
325 }
326
327
328 /*
329 ==================
330 V_cshift_f
331 ==================
332 */
333 void V_cshift_f (void)
334 {
335         cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
336         cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
337         cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
338         cshift_empty.percent = atoi(Cmd_Argv(4));
339 }
340
341
342 /*
343 ==================
344 V_BonusFlash_f
345
346 When you run over an item, the server sends this command
347 ==================
348 */
349 void V_BonusFlash_f (void)
350 {
351         if (gl_polyblend.value)
352         {
353                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
354                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
355                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
356                 cl.cshifts[CSHIFT_BONUS].percent = 50;
357         }
358 }
359
360 /*
361 =============
362 V_SetContentsColor
363
364 Underwater, lava, etc each has a color shift
365 =============
366 */
367 void V_SetContentsColor (int contents)
368 {
369         cshift_t* c;
370         c = &cl.cshifts[CSHIFT_CONTENTS]; // just to shorten the code below
371         if (!gl_polyblend.value)
372         {
373                 c->percent = 0;
374                 return;
375         }
376         switch (contents)
377         {
378         case CONTENTS_EMPTY:
379         case CONTENTS_SOLID:
380                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
381                 c->destcolor[0] = cshift_empty.destcolor[0];
382                 c->destcolor[1] = cshift_empty.destcolor[1];
383                 c->destcolor[2] = cshift_empty.destcolor[2];
384                 c->percent = cshift_empty.percent;
385                 break;
386         case CONTENTS_LAVA:
387                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
388                 c->destcolor[0] = cshift_lava.destcolor[0];
389                 c->destcolor[1] = cshift_lava.destcolor[1];
390                 c->destcolor[2] = cshift_lava.destcolor[2];
391                 c->percent = cshift_lava.percent;
392                 break;
393         case CONTENTS_SLIME:
394                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
395                 c->destcolor[0] = cshift_slime.destcolor[0];
396                 c->destcolor[1] = cshift_slime.destcolor[1];
397                 c->destcolor[2] = cshift_slime.destcolor[2];
398                 c->percent = cshift_slime.percent;
399                 break;
400         default:
401                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
402                 c->destcolor[0] = cshift_water.destcolor[0];
403                 c->destcolor[1] = cshift_water.destcolor[1];
404                 c->destcolor[2] = cshift_water.destcolor[2];
405                 c->percent = cshift_water.percent;
406         }
407 }
408
409 /*
410 =============
411 V_CalcPowerupCshift
412 =============
413 */
414 void V_CalcPowerupCshift (void)
415 {
416         if (!gl_polyblend.value)
417         {
418                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
419                 return;
420         }
421         if (cl.items & IT_QUAD)
422         {
423                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
424                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
425                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
426                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
427         }
428         else if (cl.items & IT_SUIT)
429         {
430                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
431                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
432                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
433                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
434         }
435         else if (cl.items & IT_INVISIBILITY)
436         {
437                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
438                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
439                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
440                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
441         }
442         else if (cl.items & IT_INVULNERABILITY)
443         {
444                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
445                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
446                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
447                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
448         }
449         else
450                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
451 }
452
453 /*
454 =============
455 V_CalcBlend
456 =============
457 */
458 // LordHavoc: fixed V_CalcBlend
459 void V_CalcBlend (void)
460 {
461         float   r, g, b, a, a2;
462         int             j;
463
464         r = 0;
465         g = 0;
466         b = 0;
467         a = 0;
468
469 //      if (gl_cshiftpercent.value)
470 //      {
471                 for (j=0 ; j<NUM_CSHIFTS ; j++) 
472                 {
473 //                      a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
474                         a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
475
476                         if (!a2)
477                                 continue;
478                         if (a2 > 1)
479                                 a2 = 1;
480                         r += (cl.cshifts[j].destcolor[0]-r) * a2;
481                         g += (cl.cshifts[j].destcolor[1]-g) * a2;
482                         b += (cl.cshifts[j].destcolor[2]-b) * a2;
483                         a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
484                 }
485                 // saturate color (to avoid blending in black)
486                 if (a)
487                 {
488                         a2 = 1 / a;
489                         r *= a2;
490                         g *= a2;
491                         b *= a2;
492                 }
493 //      }
494
495         v_blend[0] = bound(0, r * (1.0/255.0), 1);
496         v_blend[1] = bound(0, g * (1.0/255.0), 1);
497         v_blend[2] = bound(0, b * (1.0/255.0), 1);
498         v_blend[3] = bound(0, a              , 1);
499 }
500
501 /*
502 =============
503 V_UpdateBlends
504 =============
505 */
506 void V_UpdateBlends (void)
507 {
508         int             i, j;
509         qboolean        new;
510
511         V_CalcPowerupCshift ();
512         
513         new = false;
514         
515         for (i=0 ; i<NUM_CSHIFTS ; i++)
516         {
517                 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
518                 {
519                         new = true;
520                         cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
521                 }
522                 for (j=0 ; j<3 ; j++)
523                         if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
524                         {
525                                 new = true;
526                                 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
527                         }
528         }
529         
530 // drop the damage value
531         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
532         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
533                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
534
535 // drop the bonus value
536         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
537         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
538                 cl.cshifts[CSHIFT_BONUS].percent = 0;
539
540         if (!new)
541                 return;
542
543         V_CalcBlend ();
544 }
545
546 /* 
547 ============================================================================== 
548  
549                                                 VIEW RENDERING 
550  
551 ============================================================================== 
552 */ 
553
554 float angledelta (float a)
555 {
556         a = ANGLEMOD(a);
557         if (a > 180)
558                 a -= 360;
559         return a;
560 }
561
562 /*
563 ==================
564 CalcGunAngle
565 ==================
566 */
567 void CalcGunAngle (void)
568 {       
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 = cl.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.render.angles[YAW] = r_refdef.viewangles[YAW];
617         cl.viewent.render.angles[PITCH] = -r_refdef.viewangles[PITCH];
618
619         cl.viewent.render.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
620         cl.viewent.render.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
621         cl.viewent.render.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
622 }
623
624 /*
625 ==============
626 V_BoundOffsets
627 ==============
628 */
629 void V_BoundOffsets (void)
630 {
631         entity_t        *ent;
632         
633         ent = &cl_entities[cl.viewentity];
634
635 // absolutely bound refresh relative to entity clipping hull
636 // so the view can never be inside a solid wall
637
638         if (r_refdef.vieworg[0] < ent->render.origin[0] - 14)
639                 r_refdef.vieworg[0] = ent->render.origin[0] - 14;
640         else if (r_refdef.vieworg[0] > ent->render.origin[0] + 14)
641                 r_refdef.vieworg[0] = ent->render.origin[0] + 14;
642         if (r_refdef.vieworg[1] < ent->render.origin[1] - 14)
643                 r_refdef.vieworg[1] = ent->render.origin[1] - 14;
644         else if (r_refdef.vieworg[1] > ent->render.origin[1] + 14)
645                 r_refdef.vieworg[1] = ent->render.origin[1] + 14;
646         if (r_refdef.vieworg[2] < ent->render.origin[2] - 22)
647                 r_refdef.vieworg[2] = ent->render.origin[2] - 22;
648         else if (r_refdef.vieworg[2] > ent->render.origin[2] + 30)
649                 r_refdef.vieworg[2] = ent->render.origin[2] + 30;
650 }
651
652 /*
653 ==============
654 V_AddIdle
655
656 Idle swaying
657 ==============
658 */
659 void V_AddIdle (void)
660 {
661         r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
662         r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
663         r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
664 }
665
666
667 /*
668 ==============
669 V_CalcViewRoll
670
671 Roll is induced by movement and damage
672 ==============
673 */
674 void V_CalcViewRoll (void)
675 {
676         float           side;
677                 
678         side = V_CalcRoll (cl_entities[cl.viewentity].render.angles, cl.velocity);
679         r_refdef.viewangles[ROLL] += side;
680
681         if (v_dmg_time > 0)
682         {
683                 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
684                 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
685                 v_dmg_time -= cl.frametime;
686         }
687
688         if (cl.stats[STAT_HEALTH] <= 0)
689         {
690                 r_refdef.viewangles[ROLL] = 80; // dead view angle
691                 return;
692         }
693
694 }
695
696
697 /*
698 ==================
699 V_CalcIntermissionRefdef
700
701 ==================
702 */
703 void V_CalcIntermissionRefdef (void)
704 {
705         entity_t        *ent, *view;
706         float           old;
707
708 // ent is the player model (visible when out of body)
709         ent = &cl_entities[cl.viewentity];
710 // view is the weapon model (only visible from inside body)
711         view = &cl.viewent;
712
713         VectorCopy (ent->render.origin, r_refdef.vieworg);
714         VectorCopy (ent->render.angles, r_refdef.viewangles);
715         view->render.model = NULL;
716
717 // always idle in intermission
718         old = v_idlescale.value;
719         v_idlescale.value = 1;
720         V_AddIdle ();
721         v_idlescale.value = old;
722 }
723
724 /*
725 ==================
726 V_CalcRefdef
727
728 ==================
729 */
730 void V_CalcRefdef (void)
731 {
732         entity_t        *ent, *view;
733         int                     i;
734         vec3_t          forward;
735         vec3_t          angles;
736         float           bob;
737 //      static float oldz = 0;
738
739         V_DriftPitch ();
740
741 // ent is the player model (visible when out of body)
742         ent = &cl_entities[cl.viewentity];
743 // view is the weapon model (only visible from inside body)
744         view = &cl.viewent;
745
746
747         if (chase_active.value)
748         {
749                 VectorCopy (ent->render.origin, r_refdef.vieworg);
750                 VectorCopy (cl.viewangles, r_refdef.viewangles);
751                 Chase_Update ();
752                 V_AddIdle ();
753         }
754         else
755         {
756         // transform the view offset by the model's matrix to get the offset from model origin for the view
757         //      if (!chase_active.value) // LordHavoc: get rid of angle problems in chase_active mode
758         //      {
759         //              ent->render.angles[YAW] = cl.viewangles[YAW];   // the model should face the view dir
760         //              ent->render.angles[PITCH] = -cl.viewangles[PITCH];      // the model should face the view dir
761         //      }
762
763                 bob = V_CalcBob ();
764
765         // refresh position
766                 VectorCopy (ent->render.origin, r_refdef.vieworg);
767                 r_refdef.vieworg[2] += cl.viewheight + bob;
768
769                 // LordHavoc: the protocol has changed...  so this is an obsolete approach
770         // never let it sit exactly on a node line, because a water plane can
771         // dissapear when viewed with the eye exactly on it.
772         // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
773         //      r_refdef.vieworg[0] += 1.0/32;
774         //      r_refdef.vieworg[1] += 1.0/32;
775         //      r_refdef.vieworg[2] += 1.0/32;
776
777                 if (!intimerefresh)
778                         VectorCopy (cl.viewangles, r_refdef.viewangles);
779                 V_CalcViewRoll ();
780                 V_AddIdle ();
781
782         // offsets
783                 angles[PITCH] = -ent->render.angles[PITCH];     // because entity pitches are actually backward
784                 angles[YAW] = ent->render.angles[YAW];
785                 angles[ROLL] = ent->render.angles[ROLL];
786
787                 AngleVectors (angles, forward, NULL, NULL);
788
789                 V_BoundOffsets ();
790
791         // set up gun position
792                 VectorCopy (cl.viewangles, view->render.angles);
793
794                 CalcGunAngle ();
795
796                 VectorCopy (ent->render.origin, view->render.origin);
797                 view->render.origin[2] += cl.viewheight;
798
799                 for (i=0 ; i<3 ; i++)
800                 {
801                         view->render.origin[i] += forward[i]*bob*0.4;
802         //              view->render.origin[i] += right[i]*bob*0.4;
803         //              view->render.origin[i] += up[i]*bob*0.8;
804                 }
805                 view->render.origin[2] += bob;
806
807                 view->render.model = cl.model_precache[cl.stats[STAT_WEAPON]];
808                 view->render.frame = cl.stats[STAT_WEAPONFRAME];
809                 view->render.colormap = -1; // no special coloring
810
811         // set up the refresh position
812
813                 // LordHavoc: this never looked all that good to begin with...
814                 /*
815         // smooth out stair step ups
816         if (cl.onground && ent->render.origin[2] - oldz > 0)
817         {
818                 float steptime;
819
820                 steptime = cl.time - cl.oldtime;
821                 if (steptime < 0)
822         //FIXME         I_Error ("steptime < 0");
823                         steptime = 0;
824
825                 oldz += steptime * 80;
826                 if (oldz > ent->render.origin[2])
827                         oldz = ent->render.origin[2];
828                 if (ent->render.origin[2] - oldz > 12)
829                         oldz = ent->render.origin[2] - 12;
830                 r_refdef.vieworg[2] += oldz - ent->render.origin[2];
831                 view->render.origin[2] += oldz - ent->render.origin[2];
832         }
833         else
834                 oldz = ent->render.origin[2];
835                 */
836
837         // LordHavoc: origin view kick added
838                 if (!intimerefresh && v_punch.value)
839                 {
840                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
841                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
842                 }
843         }
844 }
845
846 /*
847 ==================
848 V_RenderView
849
850 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
851 the entity origin, so any view position inside that will be valid
852 ==================
853 */
854 void V_RenderView (void)
855 {
856         if (con_forcedup)
857                 return;
858
859         if (cl.intermission)
860                 V_CalcIntermissionRefdef ();    
861         else
862                 V_CalcRefdef ();
863
864         R_RenderView ();
865 }
866
867 //============================================================================
868
869 /*
870 =============
871 V_Init
872 =============
873 */
874 void V_Init (void)
875 {
876         Cmd_AddCommand ("v_cshift", V_cshift_f);        
877         Cmd_AddCommand ("bf", V_BonusFlash_f);
878         Cmd_AddCommand ("centerview", V_StartPitchDrift);
879
880         Cvar_RegisterVariable (&v_centermove);
881         Cvar_RegisterVariable (&v_centerspeed);
882
883         Cvar_RegisterVariable (&v_iyaw_cycle);
884         Cvar_RegisterVariable (&v_iroll_cycle);
885         Cvar_RegisterVariable (&v_ipitch_cycle);
886         Cvar_RegisterVariable (&v_iyaw_level);
887         Cvar_RegisterVariable (&v_iroll_level);
888         Cvar_RegisterVariable (&v_ipitch_level);
889
890         Cvar_RegisterVariable (&v_idlescale);
891         Cvar_RegisterVariable (&crosshair);
892         Cvar_RegisterVariable (&cl_crossx);
893         Cvar_RegisterVariable (&cl_crossy);
894 //      Cvar_RegisterVariable (&gl_cshiftpercent);
895         Cvar_RegisterVariable (&gl_polyblend);
896
897         Cvar_RegisterVariable (&cl_rollspeed);
898         Cvar_RegisterVariable (&cl_rollangle);
899         Cvar_RegisterVariable (&cl_bob);
900         Cvar_RegisterVariable (&cl_bobcycle);
901         Cvar_RegisterVariable (&cl_bobup);
902
903         Cvar_RegisterVariable (&v_kicktime);
904         Cvar_RegisterVariable (&v_kickroll);
905         Cvar_RegisterVariable (&v_kickpitch);   
906
907         Cvar_RegisterVariable (&v_punch);
908 }
909
910