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