]> icculus.org git repositories - divverent/darkplaces.git/blob - view.c
beginning to add Q2 bsp support (Q3 bsp support coming later)
[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 = {0, "cl_rollspeed", "200"};
34 cvar_t  cl_rollangle = {0, "cl_rollangle", "2.0"};
35
36 cvar_t  cl_bob = {0, "cl_bob","0.02"};
37 cvar_t  cl_bobcycle = {0, "cl_bobcycle","0.6"};
38 cvar_t  cl_bobup = {0, "cl_bobup","0.5"};
39
40 cvar_t  v_kicktime = {0, "v_kicktime", "0.5"};
41 cvar_t  v_kickroll = {0, "v_kickroll", "0.6"};
42 cvar_t  v_kickpitch = {0, "v_kickpitch", "0.6"};
43
44 cvar_t  v_iyaw_cycle = {0, "v_iyaw_cycle", "2"};
45 cvar_t  v_iroll_cycle = {0, "v_iroll_cycle", "0.5"};
46 cvar_t  v_ipitch_cycle = {0, "v_ipitch_cycle", "1"};
47 cvar_t  v_iyaw_level = {0, "v_iyaw_level", "0.3"};
48 cvar_t  v_iroll_level = {0, "v_iroll_level", "0.1"};
49 cvar_t  v_ipitch_level = {0, "v_ipitch_level", "0.3"};
50
51 cvar_t  v_idlescale = {0, "v_idlescale", "0"};
52
53 cvar_t  crosshair = {CVAR_SAVE, "crosshair", "0"};
54
55 cvar_t  v_centermove = {0, "v_centermove", "0.15"};
56 cvar_t  v_centerspeed = {0, "v_centerspeed","500"};
57
58 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
59
60
61 /*
62 ===============
63 V_CalcRoll
64
65 Used by view and sv_user
66 ===============
67 */
68 float V_CalcRoll (vec3_t angles, vec3_t velocity)
69 {
70         vec3_t  right;
71         float   sign;
72         float   side;
73         float   value;
74
75         AngleVectors (angles, NULL, right, NULL);
76         side = DotProduct (velocity, right);
77         sign = side < 0 ? -1 : 1;
78         side = fabs(side);
79
80         value = cl_rollangle.value;
81
82         if (side < cl_rollspeed.value)
83                 side = side * value / cl_rollspeed.value;
84         else
85                 side = value;
86
87         return side*sign;
88
89 }
90
91 static float V_CalcBob (void)
92 {
93         double bob, cycle;
94
95         // LordHavoc: easy case
96         if (cl_bob.value == 0)
97                 return 0;
98         if (cl_bobcycle.value == 0)
99                 return 0;
100
101         // LordHavoc: FIXME: this code is *weird*, redesign it sometime
102         cycle = cl.time  / cl_bobcycle.value;
103         cycle -= (int) cycle;
104         if (cycle < cl_bobup.value)
105                 cycle = M_PI * cycle / cl_bobup.value;
106         else
107                 cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
108
109         // bob is proportional to velocity in the xy plane
110         // (don't count Z, or jumping messes it up)
111
112         bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
113         bob = bob*0.3 + bob*0.7*sin(cycle);
114         bob = bound(-7, bob, 4);
115         return bob;
116
117 }
118
119 void V_StartPitchDrift (void)
120 {
121         if (cl.laststop == cl.time)
122                 return;         // something else is keeping it from drifting
123
124         if (cl.nodrift || !cl.pitchvel)
125         {
126                 cl.pitchvel = v_centerspeed.value;
127                 cl.nodrift = false;
128                 cl.driftmove = 0;
129         }
130 }
131
132 void V_StopPitchDrift (void)
133 {
134         cl.laststop = cl.time;
135         cl.nodrift = true;
136         cl.pitchvel = 0;
137 }
138
139 /*
140 ===============
141 V_DriftPitch
142
143 Moves the client pitch angle towards cl.idealpitch sent by the server.
144
145 If the user is adjusting pitch manually, either with lookup/lookdown,
146 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
147
148 Drifting is enabled when the center view key is hit, mlook is released and
149 lookspring is non 0, or when
150 ===============
151 */
152 static void V_DriftPitch (void)
153 {
154         float           delta, move;
155
156         if (noclip_anglehack || !cl.onground || cls.demoplayback )
157         {
158                 cl.driftmove = 0;
159                 cl.pitchvel = 0;
160                 return;
161         }
162
163 // don't count small mouse motion
164         if (cl.nodrift)
165         {
166                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
167                         cl.driftmove = 0;
168                 else
169                         cl.driftmove += cl.frametime;
170
171                 if ( cl.driftmove > v_centermove.value)
172                 {
173                         V_StartPitchDrift ();
174                 }
175                 return;
176         }
177
178         delta = cl.idealpitch - cl.viewangles[PITCH];
179
180         if (!delta)
181         {
182                 cl.pitchvel = 0;
183                 return;
184         }
185
186         move = cl.frametime * cl.pitchvel;
187         cl.pitchvel += cl.frametime * v_centerspeed.value;
188
189         if (delta > 0)
190         {
191                 if (move > delta)
192                 {
193                         cl.pitchvel = 0;
194                         move = delta;
195                 }
196                 cl.viewangles[PITCH] += move;
197         }
198         else if (delta < 0)
199         {
200                 if (move > -delta)
201                 {
202                         cl.pitchvel = 0;
203                         move = -delta;
204                 }
205                 cl.viewangles[PITCH] -= move;
206         }
207 }
208
209
210 /*
211 ==============================================================================
212
213                                                 SCREEN FLASHES
214
215 ==============================================================================
216 */
217
218
219 /*
220 ===============
221 V_ParseDamage
222 ===============
223 */
224 void V_ParseDamage (void)
225 {
226         int i, armor, blood;
227         vec3_t from;
228         //vec3_t forward, right;
229         vec3_t localfrom;
230         entity_t *ent;
231         //float side;
232         float count;
233
234         armor = MSG_ReadByte ();
235         blood = MSG_ReadByte ();
236         for (i=0 ; i<3 ; i++)
237                 from[i] = MSG_ReadCoord ();
238
239         count = blood*0.5 + armor*0.5;
240         if (count < 10)
241                 count = 10;
242
243         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
244
245         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
246         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
247                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
248         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
249                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
250
251         if (armor > blood)
252         {
253                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
254                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
255                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
256         }
257         else if (armor)
258         {
259                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
260                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
261                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
262         }
263         else
264         {
265                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
266                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
267                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
268         }
269
270 //
271 // calculate view angle kicks
272 //
273         ent = &cl_entities[cl.viewentity];
274         Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
275         VectorNormalize(localfrom);
276         v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
277         v_dmg_roll = count * localfrom[1] * v_kickroll.value;
278         v_dmg_time = v_kicktime.value;
279
280         //VectorSubtract (from, ent->render.origin, from);
281         //VectorNormalize (from);
282
283         //AngleVectors (ent->render.angles, forward, right, NULL);
284
285         //side = DotProduct (from, right);
286         //v_dmg_roll = count*side*v_kickroll.value;
287
288         //side = DotProduct (from, forward);
289         //v_dmg_pitch = count*side*v_kickpitch.value;
290
291         //v_dmg_time = v_kicktime.value;
292 }
293
294 static cshift_t v_cshift;
295
296 /*
297 ==================
298 V_cshift_f
299 ==================
300 */
301 static void V_cshift_f (void)
302 {
303         v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
304         v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
305         v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
306         v_cshift.percent = atoi(Cmd_Argv(4));
307 }
308
309
310 /*
311 ==================
312 V_BonusFlash_f
313
314 When you run over an item, the server sends this command
315 ==================
316 */
317 static void V_BonusFlash_f (void)
318 {
319         cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
320         cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
321         cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
322         cl.cshifts[CSHIFT_BONUS].percent = 50;
323 }
324
325 /*
326 =============
327 V_UpdateBlends
328 =============
329 */
330 void V_UpdateBlends (void)
331 {
332         float   r, g, b, a, a2;
333         int             j;
334
335         if (cls.signon != SIGNONS)
336         {
337                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
338                 cl.cshifts[CSHIFT_BONUS].percent = 0;
339                 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
340                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
341                 r_refdef.viewblend[0] = 0;
342                 r_refdef.viewblend[1] = 0;
343                 r_refdef.viewblend[2] = 0;
344                 r_refdef.viewblend[3] = 0;
345                 return;
346         }
347
348         // drop the damage value
349         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
350         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
351                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
352
353         // drop the bonus value
354         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
355         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
356                 cl.cshifts[CSHIFT_BONUS].percent = 0;
357
358         // set contents color
359         switch (Mod_PointContents (r_refdef.vieworg, cl.worldmodel))
360         {
361         case CONTENTS_EMPTY:
362         case CONTENTS_SOLID:
363                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
364                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
365                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
366                 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
367                 break;
368         case CONTENTS_LAVA:
369                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
370                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
371                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
372                 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
373                 break;
374         case CONTENTS_SLIME:
375                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
376                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
377                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
378                 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
379                 break;
380         default:
381                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
382                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
383                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
384                 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
385         }
386
387         if (cl.items & IT_QUAD)
388         {
389                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
390                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
391                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
392                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
393         }
394         else if (cl.items & IT_SUIT)
395         {
396                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
397                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
398                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
399                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
400         }
401         else if (cl.items & IT_INVISIBILITY)
402         {
403                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
404                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
405                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
406                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
407         }
408         else if (cl.items & IT_INVULNERABILITY)
409         {
410                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
411                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
412                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
413                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
414         }
415         else
416                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
417
418         // LordHavoc: fixed V_CalcBlend
419         r = 0;
420         g = 0;
421         b = 0;
422         a = 0;
423
424         for (j=0 ; j<NUM_CSHIFTS ; j++)
425         {
426                 a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
427
428                 if (a2 < 0)
429                         continue;
430                 if (a2 > 1)
431                         a2 = 1;
432                 r += (cl.cshifts[j].destcolor[0]-r) * a2;
433                 g += (cl.cshifts[j].destcolor[1]-g) * a2;
434                 b += (cl.cshifts[j].destcolor[2]-b) * a2;
435                 a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
436         }
437         // saturate color (to avoid blending in black)
438         if (a)
439         {
440                 a2 = 1 / a;
441                 r *= a2;
442                 g *= a2;
443                 b *= a2;
444         }
445
446         r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
447         r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
448         r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
449         r_refdef.viewblend[3] = bound(0, a              , 1);
450 }
451
452 /*
453 ==============================================================================
454
455                                                 VIEW RENDERING
456
457 ==============================================================================
458 */
459
460 /*
461 ==============
462 V_AddIdle
463
464 Idle swaying
465 ==============
466 */
467 static void V_AddIdle (float idle)
468 {
469         r_refdef.viewangles[ROLL] += idle * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
470         r_refdef.viewangles[PITCH] += idle * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
471         r_refdef.viewangles[YAW] += idle * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
472 }
473
474 #define MAXVIEWMODELS 32
475 extern int numviewmodels;
476 extern entity_t *viewmodels[MAXVIEWMODELS];
477 void V_LinkViewEntities(void)
478 {
479         int i;
480         //float v[3];
481         entity_t *ent;
482         matrix4x4_t matrix, matrix2;
483
484         if (numviewmodels <= 0)
485                 return;
486
487         //Matrix4x4_CreateRotate(&matrix, 1, 0, 0, r_refdef.viewangles[0]);
488         //Matrix4x4_CreateRotate(&matrix, 0, 1, 0, r_refdef.viewangles[0]);
489         //Matrix4x4_CreateRotate(&matrix, 0, 0, 1, r_refdef.viewangles[0]);
490         Matrix4x4_CreateFromQuakeEntity(&matrix, r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[0], r_refdef.viewangles[1], r_refdef.viewangles[2], 0.3);
491         for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
492         {
493                 ent = viewmodels[i];
494                 r_refdef.entities[r_refdef.numentities++] = &ent->render;
495
496                 //VectorCopy(ent->render.origin, v);
497                 //ent->render.origin[0] = v[0] * vpn[0] + v[1] * vright[0] + v[2] * vup[0] + r_refdef.vieworg[0];
498                 //ent->render.origin[1] = v[0] * vpn[1] + v[1] * vright[1] + v[2] * vup[1] + r_refdef.vieworg[1];
499                 //ent->render.origin[2] = v[0] * vpn[2] + v[1] * vright[2] + v[2] * vup[2] + r_refdef.vieworg[2];
500                 //ent->render.angles[0] = ent->render.angles[0] + r_refdef.viewangles[0];
501                 //ent->render.angles[1] = ent->render.angles[1] + r_refdef.viewangles[1];
502                 //ent->render.angles[2] = ent->render.angles[2] + r_refdef.viewangles[2];
503                 //ent->render.scale *= 0.3;
504
505                 //Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, ent->render.origin[0], ent->render.origin[1], ent->render.origin[2], ent->render.angles[0], ent->render.angles[1], ent->render.angles[2], ent->render.scale);
506                 matrix2 = ent->render.matrix;
507                 Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
508                 Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
509                 CL_BoundingBoxForEntity(&ent->render);
510         }
511 }
512
513 /*
514 ==================
515 V_CalcRefdef
516
517 ==================
518 */
519 void V_CalcRefdef (void)
520 {
521         entity_t *ent, *view;
522
523         if (cls.state != ca_connected || cls.signon != SIGNONS)
524                 return;
525
526         // ent is the player model (visible when out of body)
527         ent = &cl_entities[cl.viewentity];
528         // view is the weapon model (only visible from inside body)
529         view = &cl.viewent;
530
531         V_DriftPitch ();
532
533         VectorCopy (ent->render.origin, r_refdef.vieworg);
534         if (!intimerefresh)
535                 VectorCopy (cl.viewangles, r_refdef.viewangles);
536
537         if (cl.intermission)
538         {
539                 view->render.model = NULL;
540                 VectorCopy (ent->render.angles, r_refdef.viewangles);
541                 V_AddIdle (1);
542         }
543         else if (chase_active.value)
544         {
545                 view->render.model = NULL;
546                 r_refdef.vieworg[2] += cl.viewheight;
547                 Chase_Update ();
548                 V_AddIdle (v_idlescale.value);
549         }
550         else
551         {
552                 r_refdef.viewangles[ROLL] += V_CalcRoll (cl.viewangles, cl.velocity);
553
554                 if (v_dmg_time > 0)
555                 {
556                         r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
557                         r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
558                         v_dmg_time -= cl.frametime;
559                 }
560
561                 if (cl.stats[STAT_HEALTH] <= 0)
562                         r_refdef.viewangles[ROLL] = 80; // dead view angle
563
564                 V_AddIdle (v_idlescale.value);
565
566                 r_refdef.vieworg[2] += cl.viewheight + V_CalcBob ();
567
568                 // LordHavoc: origin view kick added
569                 if (!intimerefresh)
570                 {
571                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
572                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
573                 }
574
575                 // set up gun
576                 // (FIXME! this should be in cl_main.c with the other linking code, not view.c!)
577                 view->state_current.modelindex = cl.stats[STAT_WEAPON];
578                 view->state_current.frame = cl.stats[STAT_WEAPONFRAME];
579                 //VectorCopy(r_refdef.vieworg, view->render.origin);
580                 //view->render.angles[PITCH] = r_refdef.viewangles[PITCH] + v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
581                 //view->render.angles[YAW] = r_refdef.viewangles[YAW] - v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
582                 //view->render.angles[ROLL] = r_refdef.viewangles[ROLL] - v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
583                 //view->render.scale = 1.0 / 3.0;
584                 Matrix4x4_CreateFromQuakeEntity(&view->render.matrix, r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[PITCH] + v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value, r_refdef.viewangles[YAW] - v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value, r_refdef.viewangles[ROLL] - v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 0.3);
585                 Matrix4x4_Invert_Simple(&view->render.inversematrix, &view->render.matrix);
586                 CL_BoundingBoxForEntity(&view->render);
587                 // FIXME: this setup code is somewhat evil (CL_LerpUpdate should be private?)
588                 CL_LerpUpdate(view);
589                 view->render.colormap = -1; // no special coloring
590                 view->render.alpha = ent->render.alpha; // LordHavoc: if the player is transparent, so is the gun
591                 view->render.effects = ent->render.effects;
592                 AngleVectors(r_refdef.viewangles, vpn, vright, vup);
593
594                 // link into render entities list
595                 if (r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY) && cl.stats[STAT_HEALTH] > 0)
596                 {
597                         if (r_refdef.numentities < r_refdef.maxentities && view->render.model != NULL)
598                                 r_refdef.entities[r_refdef.numentities++] = &view->render;
599                         V_LinkViewEntities();
600                 }
601         }
602 }
603
604 //============================================================================
605
606 /*
607 =============
608 V_Init
609 =============
610 */
611 void V_Init (void)
612 {
613         Cmd_AddCommand ("v_cshift", V_cshift_f);
614         Cmd_AddCommand ("bf", V_BonusFlash_f);
615         Cmd_AddCommand ("centerview", V_StartPitchDrift);
616
617         Cvar_RegisterVariable (&v_centermove);
618         Cvar_RegisterVariable (&v_centerspeed);
619
620         Cvar_RegisterVariable (&v_iyaw_cycle);
621         Cvar_RegisterVariable (&v_iroll_cycle);
622         Cvar_RegisterVariable (&v_ipitch_cycle);
623         Cvar_RegisterVariable (&v_iyaw_level);
624         Cvar_RegisterVariable (&v_iroll_level);
625         Cvar_RegisterVariable (&v_ipitch_level);
626
627         Cvar_RegisterVariable (&v_idlescale);
628         Cvar_RegisterVariable (&crosshair);
629
630         Cvar_RegisterVariable (&cl_rollspeed);
631         Cvar_RegisterVariable (&cl_rollangle);
632         Cvar_RegisterVariable (&cl_bob);
633         Cvar_RegisterVariable (&cl_bobcycle);
634         Cvar_RegisterVariable (&cl_bobup);
635
636         Cvar_RegisterVariable (&v_kicktime);
637         Cvar_RegisterVariable (&v_kickroll);
638         Cvar_RegisterVariable (&v_kickpitch);
639 }
640