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