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