]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
Unify the command and cvar flags, under the CF_ prefix.
[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 #include "cl_collision.h"
24 #include "image.h"
25
26 /*
27
28 The view is allowed to move slightly from it's true position for bobbing,
29 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
30 entities sent from the server may not include everything in the pvs, especially
31 when crossing a water boudnary.
32
33 */
34
35 cvar_t cl_rollspeed = {CF_CLIENT, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
36 cvar_t cl_rollangle = {CF_CLIENT, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
37
38 cvar_t cl_bob = {CF_CLIENT | CF_ARCHIVE, "cl_bob","0.02", "view bobbing amount"};
39 cvar_t cl_bobcycle = {CF_CLIENT | CF_ARCHIVE, "cl_bobcycle","0.6", "view bobbing speed"};
40 cvar_t cl_bobup = {CF_CLIENT | CF_ARCHIVE, "cl_bobup","0.5", "view bobbing adjustment that makes the up or down swing of the bob last longer"};
41 cvar_t cl_bob2 = {CF_CLIENT | CF_ARCHIVE, "cl_bob2","0", "sideways view bobbing amount"};
42 cvar_t cl_bob2cycle = {CF_CLIENT | CF_ARCHIVE, "cl_bob2cycle","0.6", "sideways view bobbing speed"};
43 cvar_t cl_bob2smooth = {CF_CLIENT | CF_ARCHIVE, "cl_bob2smooth","0.05", "how fast the view goes back when you stop touching the ground"};
44 cvar_t cl_bobfall = {CF_CLIENT | CF_ARCHIVE, "cl_bobfall","0", "how much the view swings down when falling (influenced by the speed you hit the ground with)"};
45 cvar_t cl_bobfallcycle = {CF_CLIENT | CF_ARCHIVE, "cl_bobfallcycle","3", "speed of the bobfall swing"};
46 cvar_t cl_bobfallminspeed = {CF_CLIENT | CF_ARCHIVE, "cl_bobfallminspeed","200", "necessary amount of speed for bob-falling to occur"};
47 cvar_t cl_bobmodel = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel", "1", "enables gun bobbing"};
48 cvar_t cl_bobmodel_side = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_side", "0", "gun bobbing sideways sway amount"};
49 cvar_t cl_bobmodel_up = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_up", "0", "gun bobbing upward movement amount"};
50 cvar_t cl_bobmodel_forward = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_forward", "0.25", "gun bobbing forward movement amount"};
51 cvar_t cl_bobmodel_classic = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_classic", "1", "classic Quake-style forward gun bobbing"};
52 cvar_t cl_bobmodel_speed = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_speed", "6", "gun bobbing speed"};
53 cvar_t cl_bob_limit = {CF_CLIENT | CF_ARCHIVE, "cl_bob_limit", "4", "limits bobbing to this much distance from view_ofs"};
54 cvar_t cl_bob_limit_heightcheck = {CF_CLIENT | CF_ARCHIVE, "cl_bob_limit_heightcheck", "0", "check ceiling and floor height against cl_bob_limit and scale down all view bobbing if could result in camera being in solid"};
55 cvar_t cl_bob_limit_heightcheck_dontcrosswatersurface = {CF_CLIENT | CF_ARCHIVE, "cl_bob_limit_heightcheck_dontcrosswatersurface", "1", "limit cl_bob_limit to not crossing liquid surfaces also"};
56 cvar_t cl_bob_velocity_limit = {CF_CLIENT | CF_ARCHIVE, "cl_bob_velocity_limit", "400", "limits the xyspeed value in the bobbing code"};
57
58 cvar_t cl_leanmodel = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel", "0", "enables gun leaning"};
59 cvar_t cl_leanmodel_side_speed = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_speed", "0.7", "gun leaning sideways speed"};
60 cvar_t cl_leanmodel_side_limit = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_limit", "35", "gun leaning sideways limit"};
61 cvar_t cl_leanmodel_side_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_highpass1", "30", "gun leaning sideways pre-highpass in 1/s"};
62 cvar_t cl_leanmodel_side_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_highpass", "3", "gun leaning sideways highpass in 1/s"};
63 cvar_t cl_leanmodel_side_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_lowpass", "20", "gun leaning sideways lowpass in 1/s"};
64 cvar_t cl_leanmodel_up_speed = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_speed", "0.65", "gun leaning upward speed"};
65 cvar_t cl_leanmodel_up_limit = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_limit", "50", "gun leaning upward limit"};
66 cvar_t cl_leanmodel_up_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_highpass1", "5", "gun leaning upward pre-highpass in 1/s"};
67 cvar_t cl_leanmodel_up_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_highpass", "15", "gun leaning upward highpass in 1/s"};
68 cvar_t cl_leanmodel_up_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_lowpass", "20", "gun leaning upward lowpass in 1/s"};
69
70 cvar_t cl_followmodel = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel", "0", "enables gun following"};
71 cvar_t cl_followmodel_side_speed = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_speed", "0.25", "gun following sideways speed"};
72 cvar_t cl_followmodel_side_limit = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_limit", "6", "gun following sideways limit"};
73 cvar_t cl_followmodel_side_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_highpass1", "30", "gun following sideways pre-highpass in 1/s"};
74 cvar_t cl_followmodel_side_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_highpass", "5", "gun following sideways highpass in 1/s"};
75 cvar_t cl_followmodel_side_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_lowpass", "10", "gun following sideways lowpass in 1/s"};
76 cvar_t cl_followmodel_up_speed = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_speed", "0.5", "gun following upward speed"};
77 cvar_t cl_followmodel_up_limit = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_limit", "5", "gun following upward limit"};
78 cvar_t cl_followmodel_up_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_highpass1", "60", "gun following upward pre-highpass in 1/s"};
79 cvar_t cl_followmodel_up_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_highpass", "2", "gun following upward highpass in 1/s"};
80 cvar_t cl_followmodel_up_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_lowpass", "10", "gun following upward lowpass in 1/s"};
81
82 cvar_t cl_viewmodel_scale = {CF_CLIENT, "cl_viewmodel_scale", "1", "changes size of gun model, lower values prevent poking into walls but cause strange artifacts on lighting and especially r_stereo/vid_stereobuffer options where the size of the gun becomes visible"};
83
84 cvar_t v_kicktime = {CF_CLIENT, "v_kicktime", "0.5", "how long a view kick from damage lasts"};
85 cvar_t v_kickroll = {CF_CLIENT, "v_kickroll", "0.6", "how much a view kick from damage rolls your view"};
86 cvar_t v_kickpitch = {CF_CLIENT, "v_kickpitch", "0.6", "how much a view kick from damage pitches your view"};
87
88 cvar_t v_iyaw_cycle = {CF_CLIENT, "v_iyaw_cycle", "2", "v_idlescale yaw speed"};
89 cvar_t v_iroll_cycle = {CF_CLIENT, "v_iroll_cycle", "0.5", "v_idlescale roll speed"};
90 cvar_t v_ipitch_cycle = {CF_CLIENT, "v_ipitch_cycle", "1", "v_idlescale pitch speed"};
91 cvar_t v_iyaw_level = {CF_CLIENT, "v_iyaw_level", "0.3", "v_idlescale yaw amount"};
92 cvar_t v_iroll_level = {CF_CLIENT, "v_iroll_level", "0.1", "v_idlescale roll amount"};
93 cvar_t v_ipitch_level = {CF_CLIENT, "v_ipitch_level", "0.3", "v_idlescale pitch amount"};
94
95 cvar_t v_idlescale = {CF_CLIENT, "v_idlescale", "0", "how much of the quake 'drunken view' effect to use"};
96
97 cvar_t v_isometric = {CF_CLIENT, "v_isometric", "0", "changes view to isometric (non-perspective)"};
98 cvar_t v_isometric_verticalfov = {CF_CLIENT, "v_isometric_verticalfov", "512", "vertical field of view in game units (horizontal is computed using aspect ratio based on this)"};
99 cvar_t v_isometric_xx = {CF_CLIENT, "v_isometric_xx", "1", "camera matrix"};
100 cvar_t v_isometric_xy = {CF_CLIENT, "v_isometric_xy", "0", "camera matrix"};
101 cvar_t v_isometric_xz = {CF_CLIENT, "v_isometric_xz", "0", "camera matrix"};
102 cvar_t v_isometric_yx = {CF_CLIENT, "v_isometric_yx", "0", "camera matrix"};
103 cvar_t v_isometric_yy = {CF_CLIENT, "v_isometric_yy", "1", "camera matrix"};
104 cvar_t v_isometric_yz = {CF_CLIENT, "v_isometric_yz", "0", "camera matrix"};
105 cvar_t v_isometric_zx = {CF_CLIENT, "v_isometric_zx", "0", "camera matrix"};
106 cvar_t v_isometric_zy = {CF_CLIENT, "v_isometric_zy", "0", "camera matrix"};
107 cvar_t v_isometric_zz = {CF_CLIENT, "v_isometric_zz", "1", "camera matrix"};
108 cvar_t v_isometric_tx = {CF_CLIENT, "v_isometric_tx", "0", "camera position (player-relative)"};
109 cvar_t v_isometric_ty = {CF_CLIENT, "v_isometric_ty", "0", "camera position (player-relative)"};
110 cvar_t v_isometric_tz = {CF_CLIENT, "v_isometric_tz", "0", "camera position (player-relative)"};
111 cvar_t v_isometric_rot_pitch = {CF_CLIENT, "v_isometric_rot_pitch", "60", "camera rotation"};
112 cvar_t v_isometric_rot_yaw = {CF_CLIENT, "v_isometric_rot_yaw", "135", "camera rotation"};
113 cvar_t v_isometric_rot_roll = {CF_CLIENT, "v_isometric_rot_roll", "0", "camera rotation"};
114 cvar_t v_isometric_relx = {CF_CLIENT, "v_isometric_relx", "0", "camera position*forward"};
115 cvar_t v_isometric_rely = {CF_CLIENT, "v_isometric_rely", "0", "camera position*left"};
116 cvar_t v_isometric_relz = {CF_CLIENT, "v_isometric_relz", "0", "camera position*up"};
117 cvar_t v_isometric_flipcullface = {CF_CLIENT, "v_isometric_flipcullface", "0", "flips the backface culling"};
118 cvar_t v_isometric_locked_orientation = {CF_CLIENT, "v_isometric_locked_orientation", "1", "camera rotation is fixed"};
119 cvar_t v_isometric_usevieworiginculling = {CF_CLIENT, "v_isometric_usevieworiginculling", "0", "check visibility to the player location (can look pretty weird)"};
120
121 cvar_t crosshair = {CF_CLIENT | CF_ARCHIVE, "crosshair", "0", "selects crosshair to use (0 is none)"};
122
123 cvar_t v_centermove = {CF_CLIENT, "v_centermove", "0.15", "how long before the view begins to center itself (if freelook/+mlook/+jlook/+klook are off)"};
124 cvar_t v_centerspeed = {CF_CLIENT, "v_centerspeed","500", "how fast the view centers itself"};
125
126 cvar_t cl_stairsmoothspeed = {CF_CLIENT | CF_ARCHIVE, "cl_stairsmoothspeed", "160", "how fast your view moves upward/downward when running up/down stairs"};
127
128 cvar_t cl_smoothviewheight = {CF_CLIENT | CF_ARCHIVE, "cl_smoothviewheight", "0", "time of the averaging to the viewheight value so that it creates a smooth transition. higher values = longer transition, 0 for instant transition."};
129
130 cvar_t chase_back = {CF_CLIENT | CF_ARCHIVE, "chase_back", "48", "chase cam distance from the player"};
131 cvar_t chase_up = {CF_CLIENT | CF_ARCHIVE, "chase_up", "24", "chase cam distance from the player"};
132 cvar_t chase_active = {CF_CLIENT | CF_ARCHIVE, "chase_active", "0", "enables chase cam"};
133 cvar_t chase_overhead = {CF_CLIENT | CF_ARCHIVE, "chase_overhead", "0", "chase cam looks straight down if this is not zero"};
134 // GAME_GOODVSBAD2
135 cvar_t chase_stevie = {CF_CLIENT, "chase_stevie", "0", "(GOODVSBAD2 only) chase cam view from above"};
136
137 cvar_t v_deathtilt = {CF_CLIENT, "v_deathtilt", "1", "whether to use sideways view when dead"};
138 cvar_t v_deathtiltangle = {CF_CLIENT, "v_deathtiltangle", "80", "what roll angle to use when tilting the view while dead"};
139
140 // Prophecy camera pitchangle by Alexander "motorsep" Zubov
141 cvar_t chase_pitchangle = {CF_CLIENT | CF_ARCHIVE, "chase_pitchangle", "55", "chase cam pitch angle"};
142
143 cvar_t v_yshearing = {CF_CLIENT, "v_yshearing", "0", "be all out of gum (set this to the maximum angle to allow Y shearing for - try values like 75)"};
144
145 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
146
147 int cl_punchangle_applied;
148
149 void V_StartPitchDrift (void)
150 {
151         if (cl.laststop == cl.time)
152                 return;         // something else is keeping it from drifting
153
154         if (cl.nodrift || !cl.pitchvel)
155         {
156                 cl.pitchvel = v_centerspeed.value;
157                 cl.nodrift = false;
158                 cl.driftmove = 0;
159         }
160 }
161
162 void V_StartPitchDrift_f(cmd_state_t *cmd)
163 {
164         V_StartPitchDrift();
165 }
166
167 void V_StopPitchDrift (void)
168 {
169         cl.laststop = cl.time;
170         cl.nodrift = true;
171         cl.pitchvel = 0;
172 }
173
174 /*
175 ===============
176 V_DriftPitch
177
178 Moves the client pitch angle towards cl.idealpitch sent by the server.
179
180 If the user is adjusting pitch manually, either with lookup/lookdown,
181 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
182
183 Drifting is enabled when the center view key is hit, mlook is released and
184 lookspring is non 0, or when
185 ===============
186 */
187 void V_DriftPitch (void)
188 {
189         float           delta, move;
190
191         if (noclip_anglehack || !cl.onground || cls.demoplayback )
192         {
193                 cl.driftmove = 0;
194                 cl.pitchvel = 0;
195                 return;
196         }
197
198 // don't count small mouse motion
199         if (cl.nodrift)
200         {
201                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
202                         cl.driftmove = 0;
203                 else
204                         cl.driftmove += cl.realframetime;
205
206                 if ( cl.driftmove > v_centermove.value)
207                 {
208                         V_StartPitchDrift ();
209                 }
210                 return;
211         }
212
213         delta = cl.idealpitch - cl.viewangles[PITCH];
214
215         if (!delta)
216         {
217                 cl.pitchvel = 0;
218                 return;
219         }
220
221         move = cl.realframetime * cl.pitchvel;
222         cl.pitchvel += cl.realframetime * v_centerspeed.value;
223
224         if (delta > 0)
225         {
226                 if (move > delta)
227                 {
228                         cl.pitchvel = 0;
229                         move = delta;
230                 }
231                 cl.viewangles[PITCH] += move;
232         }
233         else if (delta < 0)
234         {
235                 if (move > -delta)
236                 {
237                         cl.pitchvel = 0;
238                         move = -delta;
239                 }
240                 cl.viewangles[PITCH] -= move;
241         }
242 }
243
244
245 /*
246 ==============================================================================
247
248                                                 SCREEN FLASHES
249
250 ==============================================================================
251 */
252
253
254 /*
255 ===============
256 V_ParseDamage
257 ===============
258 */
259 void V_ParseDamage (void)
260 {
261         int armor, blood;
262         vec3_t from;
263         //vec3_t forward, right;
264         vec3_t localfrom;
265         entity_t *ent;
266         //float side;
267         float count;
268
269         armor = MSG_ReadByte(&cl_message);
270         blood = MSG_ReadByte(&cl_message);
271         MSG_ReadVector(&cl_message, from, cls.protocol);
272
273         // Send the Dmg Globals to CSQC
274         CL_VM_UpdateDmgGlobals(blood, armor, from);
275
276         count = blood*0.5 + armor*0.5;
277         if (count < 10)
278                 count = 10;
279
280         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
281
282         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
283         cl.cshifts[CSHIFT_DAMAGE].alphafade = 150;
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         // calculate view angle kicks
309         if (cl.entities[cl.viewentity].state_current.active)
310         {
311                 ent = &cl.entities[cl.viewentity];
312                 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
313                 VectorNormalize(localfrom);
314                 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
315                 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
316                 v_dmg_time = v_kicktime.value;
317         }
318 }
319
320 static cshift_t v_cshift;
321
322 /*
323 ==================
324 V_cshift_f
325 ==================
326 */
327 static void V_cshift_f(cmd_state_t *cmd)
328 {
329         v_cshift.destcolor[0] = atof(Cmd_Argv(cmd, 1));
330         v_cshift.destcolor[1] = atof(Cmd_Argv(cmd, 2));
331         v_cshift.destcolor[2] = atof(Cmd_Argv(cmd, 3));
332         v_cshift.percent = atof(Cmd_Argv(cmd, 4));
333 }
334
335
336 /*
337 ==================
338 V_BonusFlash_f
339
340 When you run over an item, the server sends this command
341 ==================
342 */
343 static void V_BonusFlash_f(cmd_state_t *cmd)
344 {
345         if(Cmd_Argc(cmd) == 1)
346         {
347                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
348                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
349                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
350                 cl.cshifts[CSHIFT_BONUS].percent = 50;
351                 cl.cshifts[CSHIFT_BONUS].alphafade = 100;
352         }
353         else if(Cmd_Argc(cmd) >= 4 && Cmd_Argc(cmd) <= 6)
354         {
355                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(cmd, 1)) * 255;
356                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(cmd, 2)) * 255;
357                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(cmd, 3)) * 255;
358                 if(Cmd_Argc(cmd) >= 5)
359                         cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(cmd, 4)) * 255; // yes, these are HEXADECIMAL percent ;)
360                 else
361                         cl.cshifts[CSHIFT_BONUS].percent = 50;
362                 if(Cmd_Argc(cmd) >= 6)
363                         cl.cshifts[CSHIFT_BONUS].alphafade = atof(Cmd_Argv(cmd, 5)) * 255;
364                 else
365                         cl.cshifts[CSHIFT_BONUS].alphafade = 100;
366         }
367         else
368                 Con_Printf("usage:\nbf, or bf R G B [A [alphafade]]\n");
369 }
370
371 /*
372 ==============================================================================
373
374                                                 VIEW RENDERING
375
376 ==============================================================================
377 */
378
379 extern matrix4x4_t viewmodelmatrix_nobob;
380 extern matrix4x4_t viewmodelmatrix_withbob;
381
382 #include "cl_collision.h"
383 #include "csprogs.h"
384
385 /*
386 ==================
387 V_CalcRefdef
388
389 ==================
390 */
391 #if 0
392 static vec3_t eyeboxmins = {-16, -16, -24};
393 static vec3_t eyeboxmaxs = { 16,  16,  32};
394 #endif
395
396 static vec_t lowpass(vec_t value, vec_t frac, vec_t *store)
397 {
398         frac = bound(0, frac, 1);
399         return (*store = *store * (1 - frac) + value * frac);
400 }
401
402 static vec_t lowpass_limited(vec_t value, vec_t frac, vec_t limit, vec_t *store)
403 {
404         lowpass(value, frac, store);
405         return (*store = bound(value - limit, *store, value + limit));
406 }
407
408 static vec_t highpass(vec_t value, vec_t frac, vec_t *store)
409 {
410         return value - lowpass(value, frac, store);
411 }
412
413 static vec_t highpass_limited(vec_t value, vec_t frac, vec_t limit, vec_t *store)
414 {
415         return value - lowpass_limited(value, frac, limit, store);
416 }
417
418 static void lowpass3(vec3_t value, vec_t fracx, vec_t fracy, vec_t fracz, vec3_t store, vec3_t out)
419 {
420         out[0] = lowpass(value[0], fracx, &store[0]);
421         out[1] = lowpass(value[1], fracy, &store[1]);
422         out[2] = lowpass(value[2], fracz, &store[2]);
423 }
424
425 static void highpass3(vec3_t value, vec_t fracx, vec_t fracy, vec_t fracz, vec3_t store, vec3_t out)
426 {
427         out[0] = highpass(value[0], fracx, &store[0]);
428         out[1] = highpass(value[1], fracy, &store[1]);
429         out[2] = highpass(value[2], fracz, &store[2]);
430 }
431
432 static void highpass3_limited(vec3_t value, vec_t fracx, vec_t limitx, vec_t fracy, vec_t limity, vec_t fracz, vec_t limitz, vec3_t store, vec3_t out)
433 {
434         out[0] = highpass_limited(value[0], fracx, limitx, &store[0]);
435         out[1] = highpass_limited(value[1], fracy, limity, &store[1]);
436         out[2] = highpass_limited(value[2], fracz, limitz, &store[2]);
437 }
438
439 /*
440  * State:
441  *   cl.bob2_smooth
442  *   cl.bobfall_speed
443  *   cl.bobfall_swing
444  *   cl.gunangles_adjustment_highpass
445  *   cl.gunangles_adjustment_lowpass
446  *   cl.gunangles_highpass
447  *   cl.gunangles_prev
448  *   cl.gunorg_adjustment_highpass
449  *   cl.gunorg_adjustment_lowpass
450  *   cl.gunorg_highpass
451  *   cl.gunorg_prev
452  *   cl.hitgroundtime
453  *   cl.lastongroundtime
454  *   cl.oldongrounbd
455  *   cl.stairsmoothtime
456  *   cl.stairsmoothz
457  *   cl.calcrefdef_prevtime
458  * Extra input:
459  *   cl.movecmd[0].time
460  *   cl.movevars_stepheight
461  *   cl.movevars_timescale
462  *   cl.oldtime
463  *   cl.punchangle
464  *   cl.punchvector
465  *   cl.qw_intermission_angles
466  *   cl.qw_intermission_origin
467  *   cl.qw_weaponkick
468  *   cls.protocol
469  *   cl.time
470  * Output:
471  *   cl.csqc_viewanglesfromengine
472  *   cl.csqc_viewmodelmatrixfromengine
473  *   cl.csqc_vieworiginfromengine
474  *   r_refdef.view.matrix
475  *   viewmodelmatrix_nobob
476  *   viewmodelmatrix_withbob
477  */
478 void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qbool teleported, qbool clonground, qbool clcmdjump, float clstatsviewheight, qbool cldead, qbool clintermission, const vec3_t clvelocity)
479 {
480         float vieworg[3], viewangles[3], smoothtime;
481         float gunorg[3], gunangles[3];
482         matrix4x4_t tmpmatrix;
483         
484         static float viewheightavg;
485         float viewheight;       
486 #if 0
487 // begin of chase camera bounding box size for proper collisions by Alexander Zubov
488         vec3_t camboxmins = {-3, -3, -3};
489         vec3_t camboxmaxs = {3, 3, 3};
490 // end of chase camera bounding box size for proper collisions by Alexander Zubov
491 #endif
492         trace_t trace;
493
494         // react to clonground state changes (for gun bob)
495         if (clonground)
496         {
497                 if (!cl.oldonground)
498                         cl.hitgroundtime = cl.movecmd[0].time;
499                 cl.lastongroundtime = cl.movecmd[0].time;
500         }
501         cl.oldonground = clonground;
502         cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime);
503
504         VectorClear(gunangles);
505         VectorClear(gunorg);
506         viewmodelmatrix_nobob = identitymatrix;
507         viewmodelmatrix_withbob = identitymatrix;
508         r_refdef.view.matrix = identitymatrix;
509
510         // player can look around, so take the origin from the entity,
511         // and the angles from the input system
512         Matrix4x4_OriginFromMatrix(entrendermatrix, vieworg);
513         VectorCopy(clviewangles, viewangles);
514
515         // calculate how much time has passed since the last V_CalcRefdef
516         smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1);
517         cl.stairsmoothtime = cl.time;
518
519         // fade damage flash
520         if (v_dmg_time > 0)
521                 v_dmg_time -= bound(0, smoothtime, 0.1);
522
523         if (clintermission)
524         {
525                 // entity is a fixed camera, just copy the matrix
526                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
527                         Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1);
528                 else
529                 {
530                         r_refdef.view.matrix = *entrendermatrix;
531                         Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight);
532                 }
533                 if (v_yshearing.value > 0)
534                         Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
535                 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
536                 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
537                 Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob);
538
539                 VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
540                 VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
541
542                 Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
543                 Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value);
544         }
545         else
546         {
547                 // smooth stair stepping, but only if clonground and enabled
548                 if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported)
549                         cl.stairsmoothz = vieworg[2];
550                 else
551                 {
552                         if (cl.stairsmoothz < vieworg[2])
553                                 vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - cl.movevars_stepheight, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]);
554                         else if (cl.stairsmoothz > vieworg[2])
555                                 vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + cl.movevars_stepheight);
556                 }
557
558                 // apply qw weapon recoil effect (this did not work in QW)
559                 // TODO: add a cvar to disable this
560                 viewangles[PITCH] += cl.qw_weaponkick;
561
562                 // apply the viewofs (even if chasecam is used)
563                 // Samual: Lets add smoothing for this too so that things like crouching are done with a transition.
564                 viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1);
565                 viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight;
566                 vieworg[2] += viewheightavg;
567
568                 if (chase_active.value)
569                 {
570                         // observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov
571                         vec_t camback, camup, dist, campitch, forward[3], chase_dest[3];
572
573                         camback = chase_back.value;
574                         camup = chase_up.value;
575                         campitch = chase_pitchangle.value;
576
577                         AngleVectors(viewangles, forward, NULL, NULL);
578
579                         if (chase_overhead.integer)
580                         {
581 #if 1
582                                 vec3_t offset;
583                                 vec3_t bestvieworg;
584 #endif
585                                 vec3_t up;
586                                 viewangles[PITCH] = 0;
587                                 AngleVectors(viewangles, forward, NULL, up);
588                                 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
589                                 chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup;
590                                 chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup;
591                                 chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
592 #if 0
593 #if 1
594                                 //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
595                                 trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
596 #else
597                                 //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
598                                 trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
599 #endif
600                                 VectorCopy(trace.endpos, vieworg);
601                                 vieworg[2] -= 8;
602 #else
603                                 // trace from first person view location to our chosen third person view location
604 #if 1
605                                 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
606 #else
607                                 trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false);
608 #endif
609                                 VectorCopy(trace.endpos, bestvieworg);
610                                 offset[2] = 0;
611                                 for (offset[0] = -16;offset[0] <= 16;offset[0] += 8)
612                                 {
613                                         for (offset[1] = -16;offset[1] <= 16;offset[1] += 8)
614                                         {
615                                                 AngleVectors(viewangles, NULL, NULL, up);
616                                                 chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0];
617                                                 chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
618                                                 chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
619 #if 1
620                                                 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
621 #else
622                                                 trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false);
623 #endif
624                                                 if (bestvieworg[2] > trace.endpos[2])
625                                                         bestvieworg[2] = trace.endpos[2];
626                                         }
627                                 }
628                                 bestvieworg[2] -= 8;
629                                 VectorCopy(bestvieworg, vieworg);
630 #endif
631                                 viewangles[PITCH] = campitch;
632                         }
633                         else
634                         {
635                                 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
636                                 {
637                                         // look straight down from high above
638                                         viewangles[PITCH] = 90;
639                                         camback = 2048;
640                                         VectorSet(forward, 0, 0, -1);
641                                 }
642
643                                 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
644                                 dist = -camback - 8;
645                                 chase_dest[0] = vieworg[0] + forward[0] * dist;
646                                 chase_dest[1] = vieworg[1] + forward[1] * dist;
647                                 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
648                                 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
649                                 VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
650                         }
651                 }
652                 else
653                 {
654                         // first person view from entity
655                         // angles
656                         if (cldead && v_deathtilt.integer)
657                                 viewangles[ROLL] = v_deathtiltangle.value;
658
659                         // Hanicef: don't apply punchangle twice if the scene is rendered more than once.
660                         if (!cl_punchangle_applied)
661                         {
662                                 VectorAdd(viewangles, cl.punchangle, viewangles);
663                                 cl_punchangle_applied = 1;
664                         }
665                         viewangles[ROLL] += Com_CalcRoll(clviewangles, clvelocity, cl_rollangle.value, cl_rollspeed.value);
666
667                         if (v_dmg_time > 0)
668                         {
669                                 viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
670                                 viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
671                         }
672                         // origin
673                         VectorAdd(vieworg, cl.punchvector, vieworg);
674                         if (!cldead)
675                         {
676                                 double xyspeed, bob, bobfall;
677                                 double bobcycle = 0, cycle; // double-precision because cl.time can be a very large number, where float would get stuttery at high time values
678                                 vec_t frametime;
679
680                                 frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale;
681
682                                 if(cl_followmodel.integer || cl_leanmodel.integer)
683                                 {
684                                         // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
685                                         if(teleported)
686                                         {
687                                                 // try to fix the first highpass; result is NOT
688                                                 // perfect! TODO find a better fix
689                                                 VectorCopy(viewangles, cl.gunangles_prev);
690                                                 VectorCopy(vieworg, cl.gunorg_prev);
691                                         }
692
693                                         // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
694                                         VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
695                                         highpass3_limited(vieworg, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_up_highpass1.value, cl_followmodel_up_limit.value, cl.gunorg_highpass, gunorg);
696                                         VectorCopy(vieworg, cl.gunorg_prev);
697                                         VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
698
699                                         // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
700                                         VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
701                                         cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5);
702                                         cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5);
703                                         cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5);
704                                         highpass3_limited(viewangles, frametime*cl_leanmodel_up_highpass1.value, cl_leanmodel_up_limit.value, frametime*cl_leanmodel_side_highpass1.value, cl_leanmodel_side_limit.value, 0, 0, cl.gunangles_highpass, gunangles);
705                                         VectorCopy(viewangles, cl.gunangles_prev);
706                                         VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
707
708                                         // 3. calculate the RAW adjustment vectors
709                                         gunorg[0] *= -cl_followmodel_side_speed.value;
710                                         gunorg[1] *= -cl_followmodel_side_speed.value;
711                                         gunorg[2] *= -cl_followmodel_up_speed.value;
712
713                                         gunangles[PITCH] *= -cl_leanmodel_up_speed.value;
714                                         gunangles[YAW] *= -cl_leanmodel_side_speed.value;
715                                         gunangles[ROLL] = 0;
716
717                                         // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
718                                         //    trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
719                                         highpass3(gunorg, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_up_highpass.value, cl.gunorg_adjustment_highpass, gunorg);
720                                         lowpass3(gunorg, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_up_lowpass.value, cl.gunorg_adjustment_lowpass, gunorg);
721                                         // we assume here: PITCH = 0, YAW = 1, ROLL = 2
722                                         highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles);
723                                         lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles);
724
725                                         // 5. use the adjusted vectors
726                                         VectorAdd(vieworg, gunorg, gunorg);
727                                         VectorAdd(viewangles, gunangles, gunangles);
728                                 }
729                                 else
730                                 {
731                                         // Just initialize gunorg/gunangles.
732                                         VectorCopy(vieworg, gunorg);
733                                         VectorCopy(viewangles, gunangles);
734                                 }
735
736                                 // bounded XY speed, used by several effects below
737                                 xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), cl_bob_velocity_limit.value);
738
739                                 // vertical view bobbing code
740                                 if (cl_bob.value && cl_bobcycle.value)
741                                 {
742                                         float bob_limit = cl_bob_limit.value;
743
744                                         if (cl_bob_limit_heightcheck.integer)
745                                         {
746                                                 // use traces to determine what range the view can bob in, and scale down the bob as needed
747                                                 float trace1fraction;
748                                                 float trace2fraction;
749                                                 vec3_t bob_height_check_dest;
750
751                                                 // these multipliers are expanded a bit (the actual bob sin range is from -0.4 to 1.0) to reduce nearclip issues, especially on water surfaces
752                                                 bob_height_check_dest[0] = vieworg[0];
753                                                 bob_height_check_dest[1] = vieworg[1];
754                                                 bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * 1.1f;
755                                                 trace = CL_TraceLine(vieworg, bob_height_check_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY | (cl_bob_limit_heightcheck_dontcrosswatersurface.integer ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
756                                                 trace1fraction = trace.fraction;
757
758                                                 bob_height_check_dest[0] = vieworg[0];
759                                                 bob_height_check_dest[1] = vieworg[1];
760                                                 bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * -0.5f;
761                                                 trace = CL_TraceLine(vieworg, bob_height_check_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY | (cl_bob_limit_heightcheck_dontcrosswatersurface.integer ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
762                                                 trace2fraction = trace.fraction;
763
764                                                 bob_limit *= min(trace1fraction, trace2fraction);
765                                         }
766
767                                         // LadyHavoc: this code is *weird*, but not replacable (I think it
768                                         // should be done in QC on the server, but oh well, quake is quake)
769                                         // LadyHavoc: figured out bobup: the time at which the sin is at 180
770                                         // degrees (which allows lengthening or squishing the peak or valley)
771                                         cycle = cl.time / cl_bobcycle.value;
772                                         cycle -= (int) cycle;
773                                         if (cycle < cl_bobup.value)
774                                                 bobcycle = cycle = sin(M_PI * cycle / cl_bobup.value);
775                                         else
776                                                 bobcycle = cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
777                                         // bob is proportional to velocity in the xy plane
778                                         // (don't count Z, or jumping messes it up)
779                                         bob = xyspeed * cl_bob.value;
780                                         bob = bob*0.3 + bob*0.7*cycle;
781                                         bob = bound(-7, bob, bob_limit);
782                                         vieworg[2] += bob;
783                                         // we also need to adjust gunorg, or this appears like pushing the gun!
784                                         // In the old code, this was applied to vieworg BEFORE copying to gunorg,
785                                         // but this is not viable with the new followmodel code as that would mean
786                                         // that followmodel would work on the munged-by-bob vieworg and do feedback
787                                         gunorg[2] += bob;
788                                 }
789
790                                 // horizontal view bobbing code
791                                 if (cl_bob2.value && cl_bob2cycle.value)
792                                 {
793                                         vec3_t bob2vel;
794                                         vec3_t forward, right, up;
795                                         float side, front;
796
797                                         cycle = cl.time / cl_bob2cycle.value;
798                                         cycle -= (int) cycle;
799                                         if (cycle < 0.5)
800                                                 cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin
801                                         else
802                                                 cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5);
803                                         bob = cl_bob2.value * cycle;
804
805                                         // this value slowly decreases from 1 to 0 when we stop touching the ground.
806                                         // The cycle is later multiplied with it so the view smooths back to normal
807                                         if (clonground && !clcmdjump) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping
808                                                 cl.bob2_smooth = 1;
809                                         else
810                                         {
811                                                 if(cl.bob2_smooth > 0)
812                                                         cl.bob2_smooth -= bound(0, cl_bob2smooth.value, 1);
813                                                 else
814                                                         cl.bob2_smooth = 0;
815                                         }
816
817                                         // calculate the front and side of the player between the X and Y axes
818                                         AngleVectors(viewangles, forward, right, up);
819                                         // now get the speed based on those angles. The bounds should match the same value as xyspeed's
820                                         side = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, right) * cl.bob2_smooth, cl_bob_velocity_limit.value);
821                                         front = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, forward) * cl.bob2_smooth, cl_bob_velocity_limit.value);
822                                         VectorScale(forward, bob, forward);
823                                         VectorScale(right, bob, right);
824                                         // we use side with forward and front with right, so the bobbing goes
825                                         // to the side when we walk forward and to the front when we strafe
826                                         VectorMAMAM(side, forward, front, right, 0, up, bob2vel);
827                                         vieworg[0] += bob2vel[0];
828                                         vieworg[1] += bob2vel[1];
829                                         // we also need to adjust gunorg, or this appears like pushing the gun!
830                                         // In the old code, this was applied to vieworg BEFORE copying to gunorg,
831                                         // but this is not viable with the new followmodel code as that would mean
832                                         // that followmodel would work on the munged-by-bob vieworg and do feedback
833                                         gunorg[0] += bob2vel[0];
834                                         gunorg[1] += bob2vel[1];
835                                 }
836
837                                 // fall bobbing code
838                                 // causes the view to swing down and back up when touching the ground
839                                 if (cl_bobfall.value && cl_bobfallcycle.value)
840                                 {
841                                         if (!clonground)
842                                         {
843                                                 cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1);
844                                                 if (clvelocity[2] < -cl_bobfallminspeed.value)
845                                                         cl.bobfall_swing = 1;
846                                                 else
847                                                         cl.bobfall_swing = 0; // TODO really?
848                                         }
849                                         else
850                                         {
851                                                 cl.bobfall_swing = max(0, cl.bobfall_swing - cl_bobfallcycle.value * frametime);
852
853                                                 bobfall = sin(M_PI * cl.bobfall_swing) * cl.bobfall_speed;
854                                                 vieworg[2] += bobfall;
855                                                 gunorg[2] += bobfall;
856                                         }
857                                 }
858
859                                 // gun model bobbing code
860                                 if (cl_bobmodel.value)
861                                 {
862                                         // calculate for swinging gun model
863                                         // the gun bobs when running on the ground, but doesn't bob when you're in the air.
864                                         // Sajt: I tried to smooth out the transitions between bob and no bob, which works
865                                         // for the most part, but for some reason when you go through a message trigger or
866                                         // pick up an item or anything like that it will momentarily jolt the gun.
867                                         vec3_t forward, right, up;
868                                         float bspeed;
869                                         float s;
870                                         float t;
871
872                                         s = cl.time * cl_bobmodel_speed.value;
873                                         if (clonground)
874                                         {
875                                                 if (cl.time - cl.hitgroundtime < 0.2)
876                                                 {
877                                                         // just hit the ground, speed the bob back up over the next 0.2 seconds
878                                                         t = cl.time - cl.hitgroundtime;
879                                                         t = bound(0, t, 0.2);
880                                                         t *= 5;
881                                                 }
882                                                 else
883                                                         t = 1;
884                                         }
885                                         else
886                                         {
887                                                 // recently left the ground, slow the bob down over the next 0.2 seconds
888                                                 t = cl.time - cl.lastongroundtime;
889                                                 t = 0.2 - bound(0, t, 0.2);
890                                                 t *= 5;
891                                         }
892
893                                         bspeed = xyspeed * 0.01f;
894                                         AngleVectors (gunangles, forward, right, up);
895                                         bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t;
896                                         VectorMA (gunorg, bob, right, gunorg);
897                                         bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t;
898                                         VectorMA (gunorg, bob, up, gunorg);
899                                         // Classic Quake bobbing
900                                         bob = (cl_bobmodel_classic.integer ? xyspeed * cl_bob.value * 0.25 * bobcycle : bspeed * cl_bobmodel_forward.value * cos(s * 2) * t) * cl_viewmodel_scale.value;
901                                         VectorMA (gunorg, (cl_bobmodel_classic.integer ? (bob > 1 ? 1 : bob) : bob), forward, gunorg);
902                                 }
903                         }
904                 }
905                 // calculate a view matrix for rendering the scene
906                 if (v_idlescale.value)
907                 {
908                         viewangles[0] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
909                         viewangles[1] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
910                         viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
911                 }
912                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1);
913                 if (v_yshearing.value > 0)
914                         Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
915
916                 // calculate a viewmodel matrix for use in view-attached entities
917                 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
918                 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
919
920                 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
921                 if (v_yshearing.value > 0)
922                         Matrix4x4_QuakeToDuke3D(&viewmodelmatrix_withbob, &viewmodelmatrix_withbob, v_yshearing.value);
923
924                 VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
925                 VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
926
927                 Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
928                 Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob);
929         }
930
931         cl.calcrefdef_prevtime = cl.time;
932 }
933
934 void V_CalcRefdef (void)
935 {
936         entity_t *ent;
937         qbool cldead;
938
939         if (cls.state == ca_connected && cls.signon == SIGNONS && !cl.csqc_server2csqcentitynumber[cl.viewentity])
940         {
941                 // ent is the view entity (visible when out of body)
942                 ent = &cl.entities[cl.viewentity];
943
944                 cldead = (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && cl.stats[STAT_HEALTH] != -2342);
945                 V_CalcRefdefUsing(&ent->render.matrix, cl.viewangles, !ent->persistent.trail_allowed, cl.onground, cl.cmd.jump, cl.stats[STAT_VIEWHEIGHT], cldead, cl.intermission != 0, cl.velocity); // FIXME use a better way to detect teleport/warp than trail_allowed
946         }
947         else
948         {
949                 viewmodelmatrix_nobob = identitymatrix;
950                 viewmodelmatrix_withbob = identitymatrix;
951                 cl.csqc_viewmodelmatrixfromengine = identitymatrix;
952                 r_refdef.view.matrix = identitymatrix;
953                 VectorClear(cl.csqc_vieworiginfromengine);
954                 VectorCopy(cl.viewangles, cl.csqc_viewanglesfromengine);
955         }
956 }
957
958 void V_MakeViewIsometric(void)
959 {
960         // when using isometric view to play normal games we have to rotate the camera to make the Ortho matrix do the right thing (forward as up the screen, etc)
961         matrix4x4_t relative;
962         matrix4x4_t modifiedview;
963         matrix4x4_t modify;
964         vec3_t forward, left, up, org;
965         float t[4][4];
966
967         r_refdef.view.useperspective = false;
968         r_refdef.view.usevieworiginculling = !r_trippy.value && v_isometric_usevieworiginculling.integer;
969         r_refdef.view.frustum_y = v_isometric_verticalfov.value * cl.viewzoom;
970         r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
971         r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
972         r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
973         r_refdef.view.ortho_x = r_refdef.view.frustum_x; // used by VM_CL_R_SetView
974         r_refdef.view.ortho_y = r_refdef.view.frustum_y; // used by VM_CL_R_SetView
975
976         t[0][0] = v_isometric_xx.value;
977         t[0][1] = v_isometric_xy.value;
978         t[0][2] = v_isometric_xz.value;
979         t[0][3] = 0.0f;
980         t[1][0] = v_isometric_yx.value;
981         t[1][1] = v_isometric_yy.value;
982         t[1][2] = v_isometric_yz.value;
983         t[1][3] = 0.0f;
984         t[2][0] = v_isometric_zx.value;
985         t[2][1] = v_isometric_zy.value;
986         t[2][2] = v_isometric_zz.value;
987         t[2][3] = 0.0f;
988         t[3][0] = 0.0f;
989         t[3][1] = 0.0f;
990         t[3][2] = 0.0f;
991         t[3][3] = 1.0f;
992         Matrix4x4_FromArrayFloatGL(&modify, t[0]);
993
994         // if the orientation is locked, extract the origin and create just a translate matrix to start with
995         if (v_isometric_locked_orientation.integer)
996         {
997                 vec3_t vx, vy, vz, origin;
998                 Matrix4x4_ToVectors(&r_refdef.view.matrix, vx, vy, vz, origin);
999                 Matrix4x4_CreateTranslate(&r_refdef.view.matrix, origin[0], origin[1], origin[2]);
1000         }
1001
1002         Matrix4x4_Concat(&modifiedview, &r_refdef.view.matrix, &modify);
1003         Matrix4x4_CreateFromQuakeEntity(&relative, v_isometric_tx.value, v_isometric_ty.value, v_isometric_tz.value, v_isometric_rot_pitch.value, v_isometric_rot_yaw.value, v_isometric_rot_roll.value, 1.0f);
1004         Matrix4x4_Concat(&r_refdef.view.matrix, &modifiedview, &relative);
1005         Matrix4x4_ToVectors(&r_refdef.view.matrix, forward, left, up, org);
1006         VectorMAMAMAM(1.0f, org, v_isometric_relx.value, forward, v_isometric_rely.value, left, v_isometric_relz.value, up, org);
1007         Matrix4x4_FromVectors(&r_refdef.view.matrix, forward, left, up, org);
1008
1009         if (v_isometric_flipcullface.integer)
1010         {
1011                 int a = r_refdef.view.cullface_front;
1012                 r_refdef.view.cullface_front = r_refdef.view.cullface_back;
1013                 r_refdef.view.cullface_back = a;
1014         }
1015 }
1016
1017
1018 void V_FadeViewFlashs(void)
1019 {
1020         // don't flash if time steps backwards
1021         if (cl.time <= cl.oldtime)
1022                 return;
1023         // drop the damage value
1024         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_DAMAGE].alphafade;
1025         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
1026                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
1027         // drop the bonus value
1028         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_BONUS].alphafade;
1029         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
1030                 cl.cshifts[CSHIFT_BONUS].percent = 0;
1031 }
1032
1033 void V_CalcViewBlend(void)
1034 {
1035         float a2;
1036         int j;
1037         r_refdef.viewblend[0] = 0;
1038         r_refdef.viewblend[1] = 0;
1039         r_refdef.viewblend[2] = 0;
1040         r_refdef.viewblend[3] = 0;
1041         r_refdef.frustumscale_x = 1;
1042         r_refdef.frustumscale_y = 1;
1043         if (cls.state == ca_connected && cls.signon == SIGNONS)
1044         {
1045                 // set contents color
1046                 int supercontents;
1047                 vec3_t vieworigin;
1048                 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, vieworigin);
1049                 supercontents = CL_PointSuperContents(vieworigin);
1050                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
1051                 {
1052                         r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
1053                         r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
1054                         if (supercontents & SUPERCONTENTS_LAVA)
1055                         {
1056                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
1057                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
1058                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
1059                         }
1060                         else if (supercontents & SUPERCONTENTS_SLIME)
1061                         {
1062                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
1063                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
1064                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
1065                         }
1066                         else
1067                         {
1068                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
1069                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
1070                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
1071                         }
1072                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 * 0.5;
1073                 }
1074                 else
1075                 {
1076                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
1077                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
1078                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
1079                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
1080                 }
1081
1082                 if (gamemode != GAME_TRANSFUSION)
1083                 {
1084                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
1085                         {
1086                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
1087                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
1088                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
1089                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
1090                         }
1091                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
1092                         {
1093                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
1094                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
1095                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
1096                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
1097                         }
1098                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1099                         {
1100                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
1101                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
1102                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
1103                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
1104                         }
1105                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1106                         {
1107                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
1108                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
1109                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
1110                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
1111                         }
1112                         else
1113                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
1114                 }
1115
1116                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
1117                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
1118                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
1119                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
1120
1121                 // LadyHavoc: fixed V_CalcBlend
1122                 for (j = 0;j < NUM_CSHIFTS;j++)
1123                 {
1124                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
1125                         if (a2 > 0)
1126                         {
1127                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
1128                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
1129                         }
1130                 }
1131                 // saturate color (to avoid blending in black)
1132                 if (r_refdef.viewblend[3])
1133                 {
1134                         a2 = 1 / r_refdef.viewblend[3];
1135                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
1136                 }
1137                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0], 255.0f);
1138                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1], 255.0f);
1139                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2], 255.0f);
1140                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
1141                 if (vid.sRGB3D)
1142                 {
1143                         r_refdef.viewblend[0] = Image_LinearFloatFromsRGB(r_refdef.viewblend[0]);
1144                         r_refdef.viewblend[1] = Image_LinearFloatFromsRGB(r_refdef.viewblend[1]);
1145                         r_refdef.viewblend[2] = Image_LinearFloatFromsRGB(r_refdef.viewblend[2]);
1146                 }
1147                 else
1148                 {
1149                         r_refdef.viewblend[0] *= (1.0f/256.0f);
1150                         r_refdef.viewblend[1] *= (1.0f/256.0f);
1151                         r_refdef.viewblend[2] *= (1.0f/256.0f);
1152                 }
1153                 
1154                 // Samual: Ugly hack, I know. But it's the best we can do since
1155                 // there is no way to detect client states from the engine.
1156                 if (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && 
1157                         cl.stats[STAT_HEALTH] != -2342 && cl_deathfade.value > 0)
1158                 {
1159                         cl.deathfade += cl_deathfade.value * max(0.00001, cl.time - cl.oldtime);
1160                         cl.deathfade = bound(0.0f, cl.deathfade, 0.9f);
1161                 }
1162                 else
1163                         cl.deathfade = 0.0f;
1164
1165                 if(cl.deathfade > 0)
1166                 {
1167                         float a;
1168                         float deathfadevec[3] = {0.3f, 0.0f, 0.0f};
1169                         a = r_refdef.viewblend[3] + cl.deathfade - r_refdef.viewblend[3]*cl.deathfade;
1170                         if(a > 0)
1171                                 VectorMAM(r_refdef.viewblend[3] * (1 - cl.deathfade) / a, r_refdef.viewblend, cl.deathfade / a, deathfadevec, r_refdef.viewblend);
1172                         r_refdef.viewblend[3] = a;
1173                 }
1174         }
1175 }
1176
1177 //============================================================================
1178
1179 /*
1180 =============
1181 V_Init
1182 =============
1183 */
1184 void V_Init (void)
1185 {
1186         Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "v_cshift", V_cshift_f, "sets tint color of view");
1187         Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "bf", V_BonusFlash_f, "briefly flashes a bright color tint on view (used when items are picked up); optionally takes R G B [A [alphafade]] arguments to specify how the flash looks");
1188         Cmd_AddCommand(CF_CLIENT, "centerview", V_StartPitchDrift_f, "gradually recenter view (stop looking up/down)");
1189
1190         Cvar_RegisterVariable (&v_centermove);
1191         Cvar_RegisterVariable (&v_centerspeed);
1192
1193         Cvar_RegisterVariable (&v_iyaw_cycle);
1194         Cvar_RegisterVariable (&v_iroll_cycle);
1195         Cvar_RegisterVariable (&v_ipitch_cycle);
1196         Cvar_RegisterVariable (&v_iyaw_level);
1197         Cvar_RegisterVariable (&v_iroll_level);
1198         Cvar_RegisterVariable (&v_ipitch_level);
1199
1200         Cvar_RegisterVariable(&v_isometric);
1201         Cvar_RegisterVariable(&v_isometric_verticalfov);
1202         Cvar_RegisterVariable(&v_isometric_xx);
1203         Cvar_RegisterVariable(&v_isometric_xy);
1204         Cvar_RegisterVariable(&v_isometric_xz);
1205         Cvar_RegisterVariable(&v_isometric_yx);
1206         Cvar_RegisterVariable(&v_isometric_yy);
1207         Cvar_RegisterVariable(&v_isometric_yz);
1208         Cvar_RegisterVariable(&v_isometric_zx);
1209         Cvar_RegisterVariable(&v_isometric_zy);
1210         Cvar_RegisterVariable(&v_isometric_zz);
1211         Cvar_RegisterVariable(&v_isometric_tx);
1212         Cvar_RegisterVariable(&v_isometric_ty);
1213         Cvar_RegisterVariable(&v_isometric_tz);
1214         Cvar_RegisterVariable(&v_isometric_rot_pitch);
1215         Cvar_RegisterVariable(&v_isometric_rot_yaw);
1216         Cvar_RegisterVariable(&v_isometric_rot_roll);
1217         Cvar_RegisterVariable(&v_isometric_relx);
1218         Cvar_RegisterVariable(&v_isometric_rely);
1219         Cvar_RegisterVariable(&v_isometric_relz);
1220         Cvar_RegisterVariable(&v_isometric_flipcullface);
1221         Cvar_RegisterVariable(&v_isometric_locked_orientation);
1222         Cvar_RegisterVariable(&v_isometric_usevieworiginculling);
1223
1224         Cvar_RegisterVariable (&v_idlescale);
1225         Cvar_RegisterVariable (&crosshair);
1226
1227         Cvar_RegisterVariable (&cl_rollspeed);
1228         Cvar_RegisterVariable (&cl_rollangle);
1229         Cvar_RegisterVariable (&cl_bob);
1230         Cvar_RegisterVariable (&cl_bobcycle);
1231         Cvar_RegisterVariable (&cl_bobup);
1232         Cvar_RegisterVariable (&cl_bob2);
1233         Cvar_RegisterVariable (&cl_bob2cycle);
1234         Cvar_RegisterVariable (&cl_bob2smooth);
1235         Cvar_RegisterVariable (&cl_bobfall);
1236         Cvar_RegisterVariable (&cl_bobfallcycle);
1237         Cvar_RegisterVariable (&cl_bobfallminspeed);
1238         Cvar_RegisterVariable (&cl_bobmodel);
1239         Cvar_RegisterVariable (&cl_bobmodel_side);
1240         Cvar_RegisterVariable (&cl_bobmodel_up);
1241         Cvar_RegisterVariable (&cl_bobmodel_forward);
1242         Cvar_RegisterVariable (&cl_bobmodel_classic);
1243         Cvar_RegisterVariable (&cl_bobmodel_speed);
1244         Cvar_RegisterVariable (&cl_bob_limit);
1245         Cvar_RegisterVariable (&cl_bob_limit_heightcheck);
1246         Cvar_RegisterVariable (&cl_bob_limit_heightcheck_dontcrosswatersurface);
1247         Cvar_RegisterVariable (&cl_bob_velocity_limit);
1248
1249         Cvar_RegisterVariable (&cl_leanmodel);
1250         Cvar_RegisterVariable (&cl_leanmodel_side_speed);
1251         Cvar_RegisterVariable (&cl_leanmodel_side_limit);
1252         Cvar_RegisterVariable (&cl_leanmodel_side_highpass1);
1253         Cvar_RegisterVariable (&cl_leanmodel_side_lowpass);
1254         Cvar_RegisterVariable (&cl_leanmodel_side_highpass);
1255         Cvar_RegisterVariable (&cl_leanmodel_up_speed);
1256         Cvar_RegisterVariable (&cl_leanmodel_up_limit);
1257         Cvar_RegisterVariable (&cl_leanmodel_up_highpass1);
1258         Cvar_RegisterVariable (&cl_leanmodel_up_lowpass);
1259         Cvar_RegisterVariable (&cl_leanmodel_up_highpass);
1260
1261         Cvar_RegisterVariable (&cl_followmodel);
1262         Cvar_RegisterVariable (&cl_followmodel_side_speed);
1263         Cvar_RegisterVariable (&cl_followmodel_side_limit);
1264         Cvar_RegisterVariable (&cl_followmodel_side_highpass1);
1265         Cvar_RegisterVariable (&cl_followmodel_side_lowpass);
1266         Cvar_RegisterVariable (&cl_followmodel_side_highpass);
1267         Cvar_RegisterVariable (&cl_followmodel_up_speed);
1268         Cvar_RegisterVariable (&cl_followmodel_up_limit);
1269         Cvar_RegisterVariable (&cl_followmodel_up_highpass1);
1270         Cvar_RegisterVariable (&cl_followmodel_up_lowpass);
1271         Cvar_RegisterVariable (&cl_followmodel_up_highpass);
1272
1273         Cvar_RegisterVariable (&cl_viewmodel_scale);
1274
1275         Cvar_RegisterVariable (&v_kicktime);
1276         Cvar_RegisterVariable (&v_kickroll);
1277         Cvar_RegisterVariable (&v_kickpitch);
1278
1279         Cvar_RegisterVariable (&cl_stairsmoothspeed);
1280         
1281         Cvar_RegisterVariable (&cl_smoothviewheight);
1282
1283         Cvar_RegisterVariable (&chase_back);
1284         Cvar_RegisterVariable (&chase_up);
1285         Cvar_RegisterVariable (&chase_active);
1286         Cvar_RegisterVariable (&chase_overhead);
1287         Cvar_RegisterVariable (&chase_pitchangle);
1288         Cvar_RegisterVariable (&chase_stevie);
1289
1290         Cvar_RegisterVariable (&v_deathtilt);
1291         Cvar_RegisterVariable (&v_deathtiltangle);
1292
1293         Cvar_RegisterVariable (&v_yshearing);
1294 }
1295