2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // view.c -- player eye positioning
23 #include "cl_collision.h"
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.
35 cvar_t cl_rollspeed = {CF_CLIENT | CF_ARCHIVE, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
36 cvar_t cl_rollangle = {CF_CLIENT | CF_ARCHIVE, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
38 cvar_t cl_bob = {CF_CLIENT | CF_ARCHIVE, "cl_bob","0.02", "view bobbing amount"};
39 cvar_t cl_bobcycle = {CF_CLIENT | CF_ARCHIVE, "cl_bobcycle","0.6", "view bobbing speed"};
40 cvar_t cl_bobup = {CF_CLIENT | CF_ARCHIVE, "cl_bobup","0.5", "view bobbing adjustment that makes the up or down swing of the bob last longer"};
41 cvar_t cl_bob2 = {CF_CLIENT | CF_ARCHIVE, "cl_bob2","0", "sideways view bobbing amount"};
42 cvar_t cl_bob2cycle = {CF_CLIENT | CF_ARCHIVE, "cl_bob2cycle","0.6", "sideways view bobbing speed"};
43 cvar_t cl_bob2smooth = {CF_CLIENT | CF_ARCHIVE, "cl_bob2smooth","0.05", "how fast the view goes back when you stop touching the ground"};
44 cvar_t cl_bobfall = {CF_CLIENT | CF_ARCHIVE, "cl_bobfall","0", "how much the view swings down when falling (influenced by the speed you hit the ground with)"};
45 cvar_t cl_bobfallcycle = {CF_CLIENT | CF_ARCHIVE, "cl_bobfallcycle","3", "speed of the bobfall swing"};
46 cvar_t cl_bobfallminspeed = {CF_CLIENT | CF_ARCHIVE, "cl_bobfallminspeed","200", "necessary amount of speed for bob-falling to occur"};
47 cvar_t cl_bobmodel = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel", "1", "enables gun bobbing"};
48 cvar_t cl_bobmodel_side = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_side", "0.15", "gun bobbing sideways sway amount"};
49 cvar_t cl_bobmodel_up = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_up", "0.06", "gun bobbing upward movement amount"};
50 cvar_t cl_bobmodel_forward = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_forward", "0", "gun bobbing forward movement amount"};
51 cvar_t cl_bobmodel_classic = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_classic", "1", "classic Quake-style forward gun bobbing"};
52 cvar_t cl_bobmodel_speed = {CF_CLIENT | CF_ARCHIVE, "cl_bobmodel_speed", "7", "gun bobbing speed"};
53 cvar_t cl_bob_limit = {CF_CLIENT | CF_ARCHIVE, "cl_bob_limit", "7", "limits bobbing to this much distance from view_ofs"};
54 cvar_t cl_bob_limit_heightcheck = {CF_CLIENT | CF_ARCHIVE, "cl_bob_limit_heightcheck", "0", "check ceiling and floor height against cl_bob_limit and scale down all view bobbing if could result in camera being in solid"};
55 cvar_t cl_bob_limit_heightcheck_dontcrosswatersurface = {CF_CLIENT | CF_ARCHIVE, "cl_bob_limit_heightcheck_dontcrosswatersurface", "1", "limit cl_bob_limit to not crossing liquid surfaces also"};
56 cvar_t cl_bob_velocity_limit = {CF_CLIENT | CF_ARCHIVE, "cl_bob_velocity_limit", "400", "limits the xyspeed value in the bobbing code"};
58 cvar_t cl_leanmodel = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel", "0", "enables gun leaning"};
59 cvar_t cl_leanmodel_side_speed = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_speed", "0.7", "gun leaning sideways speed"};
60 cvar_t cl_leanmodel_side_limit = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_limit", "35", "gun leaning sideways limit"};
61 cvar_t cl_leanmodel_side_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_highpass1", "30", "gun leaning sideways pre-highpass in 1/s"};
62 cvar_t cl_leanmodel_side_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_highpass", "3", "gun leaning sideways highpass in 1/s"};
63 cvar_t cl_leanmodel_side_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_side_lowpass", "20", "gun leaning sideways lowpass in 1/s"};
64 cvar_t cl_leanmodel_up_speed = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_speed", "0.65", "gun leaning upward speed"};
65 cvar_t cl_leanmodel_up_limit = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_limit", "50", "gun leaning upward limit"};
66 cvar_t cl_leanmodel_up_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_highpass1", "5", "gun leaning upward pre-highpass in 1/s"};
67 cvar_t cl_leanmodel_up_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_highpass", "15", "gun leaning upward highpass in 1/s"};
68 cvar_t cl_leanmodel_up_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_leanmodel_up_lowpass", "20", "gun leaning upward lowpass in 1/s"};
70 cvar_t cl_followmodel = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel", "0", "enables gun following"};
71 cvar_t cl_followmodel_side_speed = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_speed", "0.25", "gun following sideways speed"};
72 cvar_t cl_followmodel_side_limit = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_limit", "6", "gun following sideways limit"};
73 cvar_t cl_followmodel_side_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_highpass1", "30", "gun following sideways pre-highpass in 1/s"};
74 cvar_t cl_followmodel_side_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_highpass", "5", "gun following sideways highpass in 1/s"};
75 cvar_t cl_followmodel_side_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_side_lowpass", "10", "gun following sideways lowpass in 1/s"};
76 cvar_t cl_followmodel_up_speed = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_speed", "0.5", "gun following upward speed"};
77 cvar_t cl_followmodel_up_limit = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_limit", "5", "gun following upward limit"};
78 cvar_t cl_followmodel_up_highpass1 = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_highpass1", "60", "gun following upward pre-highpass in 1/s"};
79 cvar_t cl_followmodel_up_highpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_highpass", "2", "gun following upward highpass in 1/s"};
80 cvar_t cl_followmodel_up_lowpass = {CF_CLIENT | CF_ARCHIVE, "cl_followmodel_up_lowpass", "10", "gun following upward lowpass in 1/s"};
82 cvar_t cl_viewmodel_scale = {CF_CLIENT, "cl_viewmodel_scale", "1", "changes size of gun model, lower values prevent poking into walls but cause strange artifacts on lighting and especially r_stereo/vid_stereobuffer options where the size of the gun becomes visible"};
84 cvar_t v_kicktime = {CF_CLIENT, "v_kicktime", "0.5", "how long a view kick from damage lasts"};
85 cvar_t v_kickroll = {CF_CLIENT, "v_kickroll", "0.6", "how much a view kick from damage rolls your view"};
86 cvar_t v_kickpitch = {CF_CLIENT, "v_kickpitch", "0.6", "how much a view kick from damage pitches your view"};
88 cvar_t v_iyaw_cycle = {CF_CLIENT, "v_iyaw_cycle", "2", "v_idlescale yaw speed"};
89 cvar_t v_iroll_cycle = {CF_CLIENT, "v_iroll_cycle", "0.5", "v_idlescale roll speed"};
90 cvar_t v_ipitch_cycle = {CF_CLIENT, "v_ipitch_cycle", "1", "v_idlescale pitch speed"};
91 cvar_t v_iyaw_level = {CF_CLIENT, "v_iyaw_level", "0.3", "v_idlescale yaw amount"};
92 cvar_t v_iroll_level = {CF_CLIENT, "v_iroll_level", "0.1", "v_idlescale roll amount"};
93 cvar_t v_ipitch_level = {CF_CLIENT, "v_ipitch_level", "0.3", "v_idlescale pitch amount"};
95 cvar_t v_idlescale = {CF_CLIENT, "v_idlescale", "0", "how much of the quake 'drunken view' effect to use"};
97 cvar_t v_isometric = {CF_CLIENT, "v_isometric", "0", "changes view to isometric (non-perspective)"};
98 cvar_t v_isometric_verticalfov = {CF_CLIENT, "v_isometric_verticalfov", "512", "vertical field of view in game units (horizontal is computed using aspect ratio based on this)"};
99 cvar_t v_isometric_xx = {CF_CLIENT, "v_isometric_xx", "1", "camera matrix"};
100 cvar_t v_isometric_xy = {CF_CLIENT, "v_isometric_xy", "0", "camera matrix"};
101 cvar_t v_isometric_xz = {CF_CLIENT, "v_isometric_xz", "0", "camera matrix"};
102 cvar_t v_isometric_yx = {CF_CLIENT, "v_isometric_yx", "0", "camera matrix"};
103 cvar_t v_isometric_yy = {CF_CLIENT, "v_isometric_yy", "1", "camera matrix"};
104 cvar_t v_isometric_yz = {CF_CLIENT, "v_isometric_yz", "0", "camera matrix"};
105 cvar_t v_isometric_zx = {CF_CLIENT, "v_isometric_zx", "0", "camera matrix"};
106 cvar_t v_isometric_zy = {CF_CLIENT, "v_isometric_zy", "0", "camera matrix"};
107 cvar_t v_isometric_zz = {CF_CLIENT, "v_isometric_zz", "1", "camera matrix"};
108 cvar_t v_isometric_tx = {CF_CLIENT, "v_isometric_tx", "0", "camera position (player-relative)"};
109 cvar_t v_isometric_ty = {CF_CLIENT, "v_isometric_ty", "0", "camera position (player-relative)"};
110 cvar_t v_isometric_tz = {CF_CLIENT, "v_isometric_tz", "0", "camera position (player-relative)"};
111 cvar_t v_isometric_rot_pitch = {CF_CLIENT, "v_isometric_rot_pitch", "60", "camera rotation"};
112 cvar_t v_isometric_rot_yaw = {CF_CLIENT, "v_isometric_rot_yaw", "135", "camera rotation"};
113 cvar_t v_isometric_rot_roll = {CF_CLIENT, "v_isometric_rot_roll", "0", "camera rotation"};
114 cvar_t v_isometric_relx = {CF_CLIENT, "v_isometric_relx", "0", "camera position*forward"};
115 cvar_t v_isometric_rely = {CF_CLIENT, "v_isometric_rely", "0", "camera position*left"};
116 cvar_t v_isometric_relz = {CF_CLIENT, "v_isometric_relz", "0", "camera position*up"};
117 cvar_t v_isometric_flipcullface = {CF_CLIENT, "v_isometric_flipcullface", "0", "flips the backface culling"};
118 cvar_t v_isometric_locked_orientation = {CF_CLIENT, "v_isometric_locked_orientation", "1", "camera rotation is fixed"};
119 cvar_t v_isometric_usevieworiginculling = {CF_CLIENT, "v_isometric_usevieworiginculling", "0", "check visibility to the player location (can look pretty weird)"};
121 cvar_t crosshair = {CF_CLIENT | CF_ARCHIVE, "crosshair", "0", "selects crosshair to use (0 is none)"};
123 cvar_t v_centermove = {CF_CLIENT, "v_centermove", "0.15", "how long before the view begins to center itself (if freelook/+mlook/+jlook/+klook are off)"};
124 cvar_t v_centerspeed = {CF_CLIENT, "v_centerspeed","500", "how fast the view centers itself"};
126 cvar_t cl_stairsmoothspeed = {CF_CLIENT | CF_ARCHIVE, "cl_stairsmoothspeed", "160", "how fast your view moves upward/downward when running up/down stairs"};
128 cvar_t cl_smoothviewheight = {CF_CLIENT | CF_ARCHIVE, "cl_smoothviewheight", "0", "time of the averaging to the viewheight value so that it creates a smooth transition. higher values = longer transition, 0 for instant transition."};
130 cvar_t chase_back = {CF_CLIENT | CF_ARCHIVE, "chase_back", "48", "chase cam distance from the player"};
131 cvar_t chase_up = {CF_CLIENT | CF_ARCHIVE, "chase_up", "24", "chase cam distance from the player"};
132 cvar_t chase_active = {CF_CLIENT | CF_ARCHIVE, "chase_active", "0", "enables chase cam"};
133 cvar_t chase_overhead = {CF_CLIENT | CF_ARCHIVE, "chase_overhead", "0", "chase cam looks straight down if this is not zero"};
135 cvar_t chase_stevie = {CF_CLIENT, "chase_stevie", "0", "(GOODVSBAD2 only) chase cam view from above"};
137 cvar_t v_deathtilt = {CF_CLIENT, "v_deathtilt", "1", "whether to use sideways view when dead"};
138 cvar_t v_deathtiltangle = {CF_CLIENT, "v_deathtiltangle", "80", "what roll angle to use when tilting the view while dead"};
140 // Prophecy camera pitchangle by Alexander "motorsep" Zubov
141 cvar_t chase_pitchangle = {CF_CLIENT | CF_ARCHIVE, "chase_pitchangle", "55", "chase cam pitch angle"};
143 cvar_t v_yshearing = {CF_CLIENT, "v_yshearing", "0", "be all out of gum (set this to the maximum angle to allow Y shearing for - try values like 75)"};
145 cvar_t r_viewmodel_quake = {CF_CLIENT | CF_ARCHIVE, "r_viewmodel_quake", "0", "Quake-style weapon viewmodel angle adjustment"};
147 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
149 int cl_punchangle_applied;
151 void V_StartPitchDrift (void)
153 if (cl.laststop == cl.time)
154 return; // something else is keeping it from drifting
156 if (cl.nodrift || !cl.pitchvel)
158 cl.pitchvel = v_centerspeed.value;
164 void V_StartPitchDrift_f(cmd_state_t *cmd)
169 void V_StopPitchDrift (void)
171 cl.laststop = cl.time;
180 Moves the client pitch angle towards cl.idealpitch sent by the server.
182 If the user is adjusting pitch manually, either with lookup/lookdown,
183 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
185 Drifting is enabled when the center view key is hit, mlook is released and
186 lookspring is non 0, or when
189 void V_DriftPitch (void)
193 if (noclip_anglehack || !cl.onground || cls.demoplayback )
200 // don't count small mouse motion
203 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
206 cl.driftmove += cl.realframetime;
208 if ( cl.driftmove > v_centermove.value)
210 V_StartPitchDrift ();
215 delta = cl.idealpitch - cl.viewangles[PITCH];
223 move = cl.realframetime * cl.pitchvel;
224 cl.pitchvel += cl.realframetime * v_centerspeed.value;
233 cl.viewangles[PITCH] += move;
242 cl.viewangles[PITCH] -= move;
248 ==============================================================================
252 ==============================================================================
261 void V_ParseDamage (void)
265 //vec3_t forward, right;
271 armor = MSG_ReadByte(&cl_message);
272 blood = MSG_ReadByte(&cl_message);
273 MSG_ReadVector(&cl_message, from, cls.protocol);
275 // Send the Dmg Globals to CSQC
276 CL_VM_UpdateDmgGlobals(blood, armor, from);
278 count = blood*0.5 + armor*0.5;
282 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
284 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
285 cl.cshifts[CSHIFT_DAMAGE].alphafade = 150;
286 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
287 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
288 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
289 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
293 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
294 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
295 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
299 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
300 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
301 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
305 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
306 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
307 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
310 // calculate view angle kicks
311 if (cl.entities[cl.viewentity].state_current.active)
313 ent = &cl.entities[cl.viewentity];
314 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
315 VectorNormalize(localfrom);
316 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
317 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
318 v_dmg_time = v_kicktime.value;
322 static cshift_t v_cshift;
329 static void V_cshift_f(cmd_state_t *cmd)
331 v_cshift.destcolor[0] = atof(Cmd_Argv(cmd, 1));
332 v_cshift.destcolor[1] = atof(Cmd_Argv(cmd, 2));
333 v_cshift.destcolor[2] = atof(Cmd_Argv(cmd, 3));
334 v_cshift.percent = atof(Cmd_Argv(cmd, 4));
342 When you run over an item, the server sends this command
345 static void V_BonusFlash_f(cmd_state_t *cmd)
347 if(Cmd_Argc(cmd) == 1)
349 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
350 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
351 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
352 cl.cshifts[CSHIFT_BONUS].percent = 50;
353 cl.cshifts[CSHIFT_BONUS].alphafade = 100;
355 else if(Cmd_Argc(cmd) >= 4 && Cmd_Argc(cmd) <= 6)
357 cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(cmd, 1)) * 255;
358 cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(cmd, 2)) * 255;
359 cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(cmd, 3)) * 255;
360 if(Cmd_Argc(cmd) >= 5)
361 cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(cmd, 4)) * 255; // yes, these are HEXADECIMAL percent ;)
363 cl.cshifts[CSHIFT_BONUS].percent = 50;
364 if(Cmd_Argc(cmd) >= 6)
365 cl.cshifts[CSHIFT_BONUS].alphafade = atof(Cmd_Argv(cmd, 5)) * 255;
367 cl.cshifts[CSHIFT_BONUS].alphafade = 100;
370 Con_Printf("usage:\nbf, or bf R G B [A [alphafade]]\n");
374 ==============================================================================
378 ==============================================================================
381 extern matrix4x4_t viewmodelmatrix_nobob;
382 extern matrix4x4_t viewmodelmatrix_withbob;
384 #include "cl_collision.h"
394 static vec3_t eyeboxmins = {-16, -16, -24};
395 static vec3_t eyeboxmaxs = { 16, 16, 32};
398 static vec_t lowpass(vec_t value, vec_t frac, vec_t *store)
400 frac = bound(0, frac, 1);
401 return (*store = *store * (1 - frac) + value * frac);
404 static vec_t lowpass_limited(vec_t value, vec_t frac, vec_t limit, vec_t *store)
406 lowpass(value, frac, store);
407 return (*store = bound(value - limit, *store, value + limit));
410 static vec_t highpass(vec_t value, vec_t frac, vec_t *store)
412 return value - lowpass(value, frac, store);
415 static vec_t highpass_limited(vec_t value, vec_t frac, vec_t limit, vec_t *store)
417 return value - lowpass_limited(value, frac, limit, store);
420 static void lowpass3(vec3_t value, vec_t fracx, vec_t fracy, vec_t fracz, vec3_t store, vec3_t out)
422 out[0] = lowpass(value[0], fracx, &store[0]);
423 out[1] = lowpass(value[1], fracy, &store[1]);
424 out[2] = lowpass(value[2], fracz, &store[2]);
427 static void highpass3(vec3_t value, vec_t fracx, vec_t fracy, vec_t fracz, vec3_t store, vec3_t out)
429 out[0] = highpass(value[0], fracx, &store[0]);
430 out[1] = highpass(value[1], fracy, &store[1]);
431 out[2] = highpass(value[2], fracz, &store[2]);
434 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)
436 out[0] = highpass_limited(value[0], fracx, limitx, &store[0]);
437 out[1] = highpass_limited(value[1], fracy, limity, &store[1]);
438 out[2] = highpass_limited(value[2], fracz, limitz, &store[2]);
446 * cl.gunangles_adjustment_highpass
447 * cl.gunangles_adjustment_lowpass
448 * cl.gunangles_highpass
450 * cl.gunorg_adjustment_highpass
451 * cl.gunorg_adjustment_lowpass
455 * cl.lastongroundtime
459 * cl.calcrefdef_prevtime
462 * cl.movevars_stepheight
463 * cl.movevars_timescale
467 * cl.qw_intermission_angles
468 * cl.qw_intermission_origin
473 * cl.csqc_viewanglesfromengine
474 * cl.csqc_viewmodelmatrixfromengine
475 * cl.csqc_vieworiginfromengine
476 * r_refdef.view.matrix
477 * viewmodelmatrix_nobob
478 * viewmodelmatrix_withbob
483 V_CalcIntermissionRefdef
487 static void V_CalcIntermissionRefdef (vec3_t vieworg, vec3_t viewangles, const matrix4x4_t *entrendermatrix, float clstatsviewheight)
489 matrix4x4_t tmpmatrix;
491 // entity is a fixed camera, just copy the matrix
492 if (cls.protocol == PROTOCOL_QUAKEWORLD)
493 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);
496 r_refdef.view.matrix = *entrendermatrix;
497 Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight);
499 if (v_yshearing.value > 0)
500 Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
501 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
502 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
503 Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob);
505 VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
506 VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
508 Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
509 Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value);
512 void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qbool teleported, qbool clonground, qbool clcmdjump, float clstatsviewheight, qbool cldead, const vec3_t clvelocity)
514 float vieworg[3], viewangles[3], smoothtime;
515 float gunorg[3], gunangles[3];
516 matrix4x4_t tmpmatrix;
517 static float viewheightavg;
521 // react to clonground state changes (for gun bob)
525 cl.hitgroundtime = cl.movecmd[0].time;
526 cl.lastongroundtime = cl.movecmd[0].time;
528 cl.oldonground = clonground;
529 cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime);
531 VectorClear(gunangles);
533 viewmodelmatrix_nobob = identitymatrix;
534 viewmodelmatrix_withbob = identitymatrix;
535 r_refdef.view.matrix = identitymatrix;
537 // player can look around, so take the origin from the entity,
538 // and the angles from the input system
539 Matrix4x4_OriginFromMatrix(entrendermatrix, vieworg);
540 VectorCopy(clviewangles, viewangles);
542 // calculate how much time has passed since the last V_CalcRefdef
543 smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1);
544 cl.stairsmoothtime = cl.time;
548 v_dmg_time -= bound(0, smoothtime, 0.1);
551 V_CalcIntermissionRefdef(vieworg, viewangles, entrendermatrix, clstatsviewheight);
554 // smooth stair stepping, but only if clonground and enabled
555 if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported)
556 cl.stairsmoothz = vieworg[2];
559 if (cl.stairsmoothz < vieworg[2])
560 vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - cl.movevars_stepheight, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]);
561 else if (cl.stairsmoothz > vieworg[2])
562 vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + cl.movevars_stepheight);
565 // apply qw weapon recoil effect (this did not work in QW)
566 // TODO: add a cvar to disable this
567 viewangles[PITCH] += cl.qw_weaponkick;
569 // apply the viewofs (even if chasecam is used)
570 // Samual: Lets add smoothing for this too so that things like crouching are done with a transition.
571 viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1);
572 viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight;
573 vieworg[2] += viewheightavg;
575 if (chase_active.value)
577 // observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov
578 vec_t camback, camup, dist, campitch, forward[3], chase_dest[3];
580 camback = chase_back.value;
581 camup = chase_up.value;
582 campitch = chase_pitchangle.value;
584 AngleVectors(viewangles, forward, NULL, NULL);
586 if (chase_overhead.integer)
591 viewangles[PITCH] = 0;
592 AngleVectors(viewangles, forward, NULL, up);
593 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
594 chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup;
595 chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup;
596 chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
597 // trace from first person view location to our chosen third person view location
598 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
599 VectorCopy(trace.endpos, bestvieworg);
601 for (offset[0] = -16;offset[0] <= 16;offset[0] += 8)
603 for (offset[1] = -16;offset[1] <= 16;offset[1] += 8)
605 AngleVectors(viewangles, NULL, NULL, up);
606 chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0];
607 chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
608 chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
609 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
610 if (bestvieworg[2] > trace.endpos[2])
611 bestvieworg[2] = trace.endpos[2];
615 VectorCopy(bestvieworg, vieworg);
616 viewangles[PITCH] = campitch;
620 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
622 // look straight down from high above
623 viewangles[PITCH] = 90;
625 VectorSet(forward, 0, 0, -1);
628 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
630 chase_dest[0] = vieworg[0] + forward[0] * dist;
631 chase_dest[1] = vieworg[1] + forward[1] * dist;
632 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
633 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
634 VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
639 // first person view from entity
641 if (cldead && v_deathtilt.integer)
642 viewangles[ROLL] = v_deathtiltangle.value;
645 VectorAdd(vieworg, cl.punchvector, vieworg);
648 double xyspeed = 0, bob = 0, bobfall = 0;
649 double cycle = 0; // double-precision because cl.time can be a very large number, where float would get stuttery at high time values
652 frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale;
654 if(cl_followmodel.integer || cl_leanmodel.integer)
656 // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
659 // try to fix the first highpass; result is NOT
660 // perfect! TODO find a better fix
661 VectorCopy(viewangles, cl.gunangles_prev);
662 VectorCopy(vieworg, cl.gunorg_prev);
665 // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
666 VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
667 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);
668 VectorCopy(vieworg, cl.gunorg_prev);
669 VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
671 // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
672 VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
673 cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5);
674 cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5);
675 cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5);
676 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);
677 VectorCopy(viewangles, cl.gunangles_prev);
678 VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
680 // 3. calculate the RAW adjustment vectors
681 gunorg[0] *= -cl_followmodel_side_speed.value;
682 gunorg[1] *= -cl_followmodel_side_speed.value;
683 gunorg[2] *= -cl_followmodel_up_speed.value;
685 gunangles[PITCH] *= -cl_leanmodel_up_speed.value;
686 gunangles[YAW] *= -cl_leanmodel_side_speed.value;
689 // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
690 // trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
691 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);
692 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);
693 // we assume here: PITCH = 0, YAW = 1, ROLL = 2
694 highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles);
695 lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles);
697 // 5. use the adjusted vectors
698 VectorAdd(vieworg, gunorg, gunorg);
699 VectorAdd(viewangles, gunangles, gunangles);
703 // Just initialize gunorg/gunangles.
704 VectorCopy(vieworg, gunorg);
705 VectorCopy(viewangles, gunangles);
708 // bounded XY speed, used by several effects below
709 xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), cl_bob_velocity_limit.value);
711 // vertical view bobbing code
712 if (cl_bob.value && cl_bobcycle.value)
714 float bob_limit = cl_bobmodel_classic.integer ? 4 : cl_bob_limit.value;
716 if (cl_bob_limit_heightcheck.integer)
718 // use traces to determine what range the view can bob in, and scale down the bob as needed
719 float trace1fraction;
720 float trace2fraction;
721 vec3_t bob_height_check_dest;
723 // 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
724 bob_height_check_dest[0] = vieworg[0];
725 bob_height_check_dest[1] = vieworg[1];
726 bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * 1.1f;
727 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);
728 trace1fraction = trace.fraction;
730 bob_height_check_dest[0] = vieworg[0];
731 bob_height_check_dest[1] = vieworg[1];
732 bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * -0.5f;
733 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);
734 trace2fraction = trace.fraction;
736 bob_limit *= min(trace1fraction, trace2fraction);
739 // LadyHavoc: this code is *weird*, but not replacable (I think it
740 // should be done in QC on the server, but oh well, quake is quake)
741 // LadyHavoc: figured out bobup: the time at which the sin is at 180
742 // degrees (which allows lengthening or squishing the peak or valley)
743 cycle = cl.time / cl_bobcycle.value;
744 cycle -= (int) cycle;
745 if (cycle < cl_bobup.value)
746 cycle = M_PI * cycle / cl_bobup.value;
748 cycle = M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
749 // bob is proportional to velocity in the xy plane
750 // (don't count Z, or jumping messes it up)
751 bob = xyspeed * cl_bob.value;
752 bob = bob*0.3 + bob*0.7*sin(cycle);
753 bob = bound(-7, bob, bob_limit);
757 // we also need to adjust gunorg, or this appears like pushing the gun!
758 // In the old code, this was applied to vieworg BEFORE copying to gunorg,
759 // but this is not viable with the new followmodel code as that would mean
760 // that followmodel would work on the munged-by-bob vieworg and do feedback
761 if(!cl_bobmodel_classic.integer)
765 // horizontal view bobbing code
766 if (cl_bob2.value && cl_bob2cycle.value)
769 vec3_t forward, right, up;
772 cycle = cl.time / cl_bob2cycle.value;
773 cycle -= (int) cycle;
775 cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin
777 cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5);
778 bob = cl_bob2.value * cycle;
780 // this value slowly decreases from 1 to 0 when we stop touching the ground.
781 // The cycle is later multiplied with it so the view smooths back to normal
782 if (clonground && !clcmdjump) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping
786 if(cl.bob2_smooth > 0)
787 cl.bob2_smooth -= bound(0, cl_bob2smooth.value, 1);
792 // calculate the front and side of the player between the X and Y axes
793 AngleVectors(viewangles, forward, right, up);
794 // now get the speed based on those angles. The bounds should match the same value as xyspeed's
795 side = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, right) * cl.bob2_smooth, cl_bob_velocity_limit.value);
796 front = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, forward) * cl.bob2_smooth, cl_bob_velocity_limit.value);
797 VectorScale(forward, bob, forward);
798 VectorScale(right, bob, right);
799 // we use side with forward and front with right, so the bobbing goes
800 // to the side when we walk forward and to the front when we strafe
801 VectorMAMAM(side, forward, front, right, 0, up, bob2vel);
802 vieworg[0] += bob2vel[0];
803 vieworg[1] += bob2vel[1];
804 // we also need to adjust gunorg, or this appears like pushing the gun!
805 // In the old code, this was applied to vieworg BEFORE copying to gunorg,
806 // but this is not viable with the new followmodel code as that would mean
807 // that followmodel would work on the munged-by-bob vieworg and do feedback
808 gunorg[0] += bob2vel[0];
809 gunorg[1] += bob2vel[1];
813 // causes the view to swing down and back up when touching the ground
814 if (cl_bobfall.value && cl_bobfallcycle.value)
818 cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1);
819 if (clvelocity[2] < -cl_bobfallminspeed.value)
820 cl.bobfall_swing = 1;
822 cl.bobfall_swing = 0; // TODO really?
826 cl.bobfall_swing = max(0, cl.bobfall_swing - cl_bobfallcycle.value * frametime);
828 bobfall = sin(M_PI * cl.bobfall_swing) * cl.bobfall_speed;
829 vieworg[2] += bobfall;
830 gunorg[2] += bobfall;
833 VectorCopy(clviewangles, viewangles);
835 // Hanicef: don't apply punchangle twice if the scene is rendered more than once.
836 if (!cl_punchangle_applied)
838 VectorAdd(viewangles, cl.punchangle, viewangles);
839 cl_punchangle_applied = 1;
841 viewangles[ROLL] += Com_CalcRoll(clviewangles, clvelocity, cl_rollangle.value, cl_rollspeed.value);
845 viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
846 viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
849 // gun model bobbing code
850 if (cl_bobmodel.value)
852 vec3_t forward, right, up;
853 AngleVectors (gunangles, forward, right, up);
855 if(!cl_bobmodel_classic.integer)
857 // calculate for swinging gun model
858 // the gun bobs when running on the ground, but doesn't bob when you're in the air.
859 // Sajt: I tried to smooth out the transitions between bob and no bob, which works
860 // for the most part, but for some reason when you go through a message trigger or
861 // pick up an item or anything like that it will momentarily jolt the gun.
866 s = cl.time * cl_bobmodel_speed.value;
869 if (cl.time - cl.hitgroundtime < 0.2)
871 // just hit the ground, speed the bob back up over the next 0.2 seconds
872 t = cl.time - cl.hitgroundtime;
873 t = bound(0, t, 0.2);
881 // recently left the ground, slow the bob down over the next 0.2 seconds
882 t = cl.time - cl.lastongroundtime;
883 t = 0.2 - bound(0, t, 0.2);
887 bspeed = xyspeed * 0.01f;
888 bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t;
889 VectorMA (gunorg, bob, right, gunorg);
890 bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t;
891 VectorMA (gunorg, bob, up, gunorg);
895 // Classic Quake bobbing
896 for (int i = 0; i < 3; i++)
897 gunorg[i] += forward[i]*bob*0.4;
900 if (r_viewmodel_quake.value)
902 if (scr_viewsize.value == 110)
904 else if (scr_viewsize.value == 100)
906 else if (scr_viewsize.value == 90)
908 else if (scr_viewsize.value == 80)
915 // calculate a view matrix for rendering the scene
916 if (v_idlescale.value)
918 viewangles[0] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
919 viewangles[1] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
920 viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
922 Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1);
923 if (v_yshearing.value > 0)
924 Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
926 // calculate a viewmodel matrix for use in view-attached entities
927 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
928 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
930 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
931 if (v_yshearing.value > 0)
932 Matrix4x4_QuakeToDuke3D(&viewmodelmatrix_withbob, &viewmodelmatrix_withbob, v_yshearing.value);
934 VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
935 VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
937 Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
938 Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob);
941 cl.calcrefdef_prevtime = cl.time;
944 void V_CalcRefdef (void)
949 if (cls.state == ca_connected && cls.signon == SIGNONS && !cl.csqc_server2csqcentitynumber[cl.viewentity])
951 // ent is the view entity (visible when out of body)
952 ent = &cl.entities[cl.viewentity];
954 cldead = (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && cl.stats[STAT_HEALTH] != -2342);
955 V_CalcRefdefUsing(&ent->render.matrix, cl.viewangles, !ent->persistent.trail_allowed, cl.onground, cl.cmd.jump, cl.stats[STAT_VIEWHEIGHT], cldead, cl.velocity); // FIXME use a better way to detect teleport/warp than trail_allowed
959 viewmodelmatrix_nobob = identitymatrix;
960 viewmodelmatrix_withbob = identitymatrix;
961 cl.csqc_viewmodelmatrixfromengine = identitymatrix;
962 r_refdef.view.matrix = identitymatrix;
963 VectorClear(cl.csqc_vieworiginfromengine);
964 VectorCopy(cl.viewangles, cl.csqc_viewanglesfromengine);
968 void V_MakeViewIsometric(void)
970 // 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)
971 matrix4x4_t relative;
972 matrix4x4_t modifiedview;
974 vec3_t forward, left, up, org;
977 r_refdef.view.useperspective = false;
978 r_refdef.view.usevieworiginculling = !r_trippy.value && v_isometric_usevieworiginculling.integer;
979 r_refdef.view.frustum_y = v_isometric_verticalfov.value * cl.viewzoom;
980 r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
981 r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
982 r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
983 r_refdef.view.ortho_x = r_refdef.view.frustum_x; // used by VM_CL_R_SetView
984 r_refdef.view.ortho_y = r_refdef.view.frustum_y; // used by VM_CL_R_SetView
986 t[0] = v_isometric_xx.value;
987 t[1] = v_isometric_xy.value;
988 t[2] = v_isometric_xz.value;
990 t[4] = v_isometric_yx.value;
991 t[5] = v_isometric_yy.value;
992 t[6] = v_isometric_yz.value;
994 t[8] = v_isometric_zx.value;
995 t[9] = v_isometric_zy.value;
996 t[10] = v_isometric_zz.value;
1002 Matrix4x4_FromArrayFloatGL(&modify, t);
1004 // if the orientation is locked, extract the origin and create just a translate matrix to start with
1005 if (v_isometric_locked_orientation.integer)
1007 vec3_t vx, vy, vz, origin;
1008 Matrix4x4_ToVectors(&r_refdef.view.matrix, vx, vy, vz, origin);
1009 Matrix4x4_CreateTranslate(&r_refdef.view.matrix, origin[0], origin[1], origin[2]);
1012 Matrix4x4_Concat(&modifiedview, &r_refdef.view.matrix, &modify);
1013 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);
1014 Matrix4x4_Concat(&r_refdef.view.matrix, &modifiedview, &relative);
1015 Matrix4x4_ToVectors(&r_refdef.view.matrix, forward, left, up, org);
1016 VectorMAMAMAM(1.0f, org, v_isometric_relx.value, forward, v_isometric_rely.value, left, v_isometric_relz.value, up, org);
1017 Matrix4x4_FromVectors(&r_refdef.view.matrix, forward, left, up, org);
1019 if (v_isometric_flipcullface.integer)
1021 int a = r_refdef.view.cullface_front;
1022 r_refdef.view.cullface_front = r_refdef.view.cullface_back;
1023 r_refdef.view.cullface_back = a;
1028 void V_FadeViewFlashs(void)
1030 // don't flash if time steps backwards
1031 if (cl.time <= cl.oldtime)
1033 // drop the damage value
1034 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_DAMAGE].alphafade;
1035 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
1036 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
1037 // drop the bonus value
1038 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_BONUS].alphafade;
1039 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
1040 cl.cshifts[CSHIFT_BONUS].percent = 0;
1043 void V_CalcViewBlend(void)
1047 r_refdef.viewblend[0] = 0;
1048 r_refdef.viewblend[1] = 0;
1049 r_refdef.viewblend[2] = 0;
1050 r_refdef.viewblend[3] = 0;
1051 r_refdef.frustumscale_x = 1;
1052 r_refdef.frustumscale_y = 1;
1053 if (cls.state == ca_connected && cls.signon == SIGNONS)
1055 // set contents color
1058 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, vieworigin);
1059 supercontents = CL_PointSuperContents(vieworigin);
1060 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
1062 cl.view_underwater = true;
1063 r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
1064 r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
1065 if (supercontents & SUPERCONTENTS_LAVA)
1067 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
1068 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
1069 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
1071 else if (supercontents & SUPERCONTENTS_SLIME)
1073 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
1074 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
1075 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
1079 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
1080 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
1081 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
1083 cl.cshifts[CSHIFT_CONTENTS].percent = 150 * 0.5;
1087 cl.view_underwater = false;
1088 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
1089 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
1090 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
1091 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
1094 if (gamemode != GAME_TRANSFUSION)
1096 if (cl.stats[STAT_ITEMS] & IT_QUAD)
1098 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
1099 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
1100 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
1101 cl.cshifts[CSHIFT_POWERUP].percent = 30;
1103 else if (cl.stats[STAT_ITEMS] & IT_SUIT)
1105 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
1106 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
1107 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
1108 cl.cshifts[CSHIFT_POWERUP].percent = 20;
1110 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1112 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
1113 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
1114 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
1115 cl.cshifts[CSHIFT_POWERUP].percent = 100;
1117 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1119 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
1120 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
1121 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
1122 cl.cshifts[CSHIFT_POWERUP].percent = 30;
1125 cl.cshifts[CSHIFT_POWERUP].percent = 0;
1128 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
1129 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
1130 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
1131 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
1133 // LadyHavoc: fixed V_CalcBlend
1134 for (j = 0;j < NUM_CSHIFTS;j++)
1136 a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
1139 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
1140 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply... took a while to find it on the web
1143 // saturate color (to avoid blending in black)
1144 if (r_refdef.viewblend[3])
1146 a2 = 1 / r_refdef.viewblend[3];
1147 VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
1149 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0], 255.0f);
1150 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1], 255.0f);
1151 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2], 255.0f);
1152 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
1155 r_refdef.viewblend[0] = Image_LinearFloatFromsRGB(r_refdef.viewblend[0]);
1156 r_refdef.viewblend[1] = Image_LinearFloatFromsRGB(r_refdef.viewblend[1]);
1157 r_refdef.viewblend[2] = Image_LinearFloatFromsRGB(r_refdef.viewblend[2]);
1161 r_refdef.viewblend[0] *= (1.0f/256.0f);
1162 r_refdef.viewblend[1] *= (1.0f/256.0f);
1163 r_refdef.viewblend[2] *= (1.0f/256.0f);
1166 // Samual: Ugly hack, I know. But it's the best we can do since
1167 // there is no way to detect client states from the engine.
1168 if (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 &&
1169 cl.stats[STAT_HEALTH] != -2342 && cl_deathfade.value > 0)
1171 cl.deathfade += cl_deathfade.value * max(0.00001, cl.time - cl.oldtime);
1172 cl.deathfade = bound(0.0f, cl.deathfade, 0.9f);
1175 cl.deathfade = 0.0f;
1177 if(cl.deathfade > 0)
1180 float deathfadevec[3] = {0.3f, 0.0f, 0.0f};
1181 a = r_refdef.viewblend[3] + cl.deathfade - r_refdef.viewblend[3]*cl.deathfade;
1183 VectorMAM(r_refdef.viewblend[3] * (1 - cl.deathfade) / a, r_refdef.viewblend, cl.deathfade / a, deathfadevec, r_refdef.viewblend);
1184 r_refdef.viewblend[3] = a;
1189 //============================================================================
1198 Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "v_cshift", V_cshift_f, "sets tint color of view");
1199 Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "bf", V_BonusFlash_f, "briefly flashes a bright color tint on view (used when items are picked up); optionally takes R G B [A [alphafade]] arguments to specify how the flash looks");
1200 Cmd_AddCommand(CF_CLIENT, "centerview", V_StartPitchDrift_f, "gradually recenter view (stop looking up/down)");
1202 Cvar_RegisterVariable (&v_centermove);
1203 Cvar_RegisterVariable (&v_centerspeed);
1205 Cvar_RegisterVariable (&v_iyaw_cycle);
1206 Cvar_RegisterVariable (&v_iroll_cycle);
1207 Cvar_RegisterVariable (&v_ipitch_cycle);
1208 Cvar_RegisterVariable (&v_iyaw_level);
1209 Cvar_RegisterVariable (&v_iroll_level);
1210 Cvar_RegisterVariable (&v_ipitch_level);
1212 Cvar_RegisterVariable(&v_isometric);
1213 Cvar_RegisterVariable(&v_isometric_verticalfov);
1214 Cvar_RegisterVariable(&v_isometric_xx);
1215 Cvar_RegisterVariable(&v_isometric_xy);
1216 Cvar_RegisterVariable(&v_isometric_xz);
1217 Cvar_RegisterVariable(&v_isometric_yx);
1218 Cvar_RegisterVariable(&v_isometric_yy);
1219 Cvar_RegisterVariable(&v_isometric_yz);
1220 Cvar_RegisterVariable(&v_isometric_zx);
1221 Cvar_RegisterVariable(&v_isometric_zy);
1222 Cvar_RegisterVariable(&v_isometric_zz);
1223 Cvar_RegisterVariable(&v_isometric_tx);
1224 Cvar_RegisterVariable(&v_isometric_ty);
1225 Cvar_RegisterVariable(&v_isometric_tz);
1226 Cvar_RegisterVariable(&v_isometric_rot_pitch);
1227 Cvar_RegisterVariable(&v_isometric_rot_yaw);
1228 Cvar_RegisterVariable(&v_isometric_rot_roll);
1229 Cvar_RegisterVariable(&v_isometric_relx);
1230 Cvar_RegisterVariable(&v_isometric_rely);
1231 Cvar_RegisterVariable(&v_isometric_relz);
1232 Cvar_RegisterVariable(&v_isometric_flipcullface);
1233 Cvar_RegisterVariable(&v_isometric_locked_orientation);
1234 Cvar_RegisterVariable(&v_isometric_usevieworiginculling);
1236 Cvar_RegisterVariable (&v_idlescale);
1237 Cvar_RegisterVariable (&crosshair);
1239 Cvar_RegisterVariable (&cl_rollspeed);
1240 Cvar_RegisterVariable (&cl_rollangle);
1241 Cvar_RegisterVariable (&cl_bob);
1242 Cvar_RegisterVariable (&cl_bobcycle);
1243 Cvar_RegisterVariable (&cl_bobup);
1244 Cvar_RegisterVariable (&cl_bob2);
1245 Cvar_RegisterVariable (&cl_bob2cycle);
1246 Cvar_RegisterVariable (&cl_bob2smooth);
1247 Cvar_RegisterVariable (&cl_bobfall);
1248 Cvar_RegisterVariable (&cl_bobfallcycle);
1249 Cvar_RegisterVariable (&cl_bobfallminspeed);
1250 Cvar_RegisterVariable (&cl_bobmodel);
1251 Cvar_RegisterVariable (&cl_bobmodel_side);
1252 Cvar_RegisterVariable (&cl_bobmodel_up);
1253 Cvar_RegisterVariable (&cl_bobmodel_forward);
1254 Cvar_RegisterVariable (&cl_bobmodel_classic);
1255 Cvar_RegisterVariable (&cl_bobmodel_speed);
1256 Cvar_RegisterVariable (&cl_bob_limit);
1257 Cvar_RegisterVariable (&cl_bob_limit_heightcheck);
1258 Cvar_RegisterVariable (&cl_bob_limit_heightcheck_dontcrosswatersurface);
1259 Cvar_RegisterVariable (&cl_bob_velocity_limit);
1261 Cvar_RegisterVariable (&cl_leanmodel);
1262 Cvar_RegisterVariable (&cl_leanmodel_side_speed);
1263 Cvar_RegisterVariable (&cl_leanmodel_side_limit);
1264 Cvar_RegisterVariable (&cl_leanmodel_side_highpass1);
1265 Cvar_RegisterVariable (&cl_leanmodel_side_lowpass);
1266 Cvar_RegisterVariable (&cl_leanmodel_side_highpass);
1267 Cvar_RegisterVariable (&cl_leanmodel_up_speed);
1268 Cvar_RegisterVariable (&cl_leanmodel_up_limit);
1269 Cvar_RegisterVariable (&cl_leanmodel_up_highpass1);
1270 Cvar_RegisterVariable (&cl_leanmodel_up_lowpass);
1271 Cvar_RegisterVariable (&cl_leanmodel_up_highpass);
1273 Cvar_RegisterVariable (&cl_followmodel);
1274 Cvar_RegisterVariable (&cl_followmodel_side_speed);
1275 Cvar_RegisterVariable (&cl_followmodel_side_limit);
1276 Cvar_RegisterVariable (&cl_followmodel_side_highpass1);
1277 Cvar_RegisterVariable (&cl_followmodel_side_lowpass);
1278 Cvar_RegisterVariable (&cl_followmodel_side_highpass);
1279 Cvar_RegisterVariable (&cl_followmodel_up_speed);
1280 Cvar_RegisterVariable (&cl_followmodel_up_limit);
1281 Cvar_RegisterVariable (&cl_followmodel_up_highpass1);
1282 Cvar_RegisterVariable (&cl_followmodel_up_lowpass);
1283 Cvar_RegisterVariable (&cl_followmodel_up_highpass);
1285 Cvar_RegisterVariable (&cl_viewmodel_scale);
1287 Cvar_RegisterVariable (&v_kicktime);
1288 Cvar_RegisterVariable (&v_kickroll);
1289 Cvar_RegisterVariable (&v_kickpitch);
1291 Cvar_RegisterVariable (&cl_stairsmoothspeed);
1293 Cvar_RegisterVariable (&cl_smoothviewheight);
1295 Cvar_RegisterVariable (&chase_back);
1296 Cvar_RegisterVariable (&chase_up);
1297 Cvar_RegisterVariable (&chase_active);
1298 Cvar_RegisterVariable (&chase_overhead);
1299 Cvar_RegisterVariable (&chase_pitchangle);
1300 Cvar_RegisterVariable (&chase_stevie);
1302 Cvar_RegisterVariable (&v_deathtilt);
1303 Cvar_RegisterVariable (&v_deathtiltangle);
1305 Cvar_RegisterVariable (&v_yshearing);
1306 Cvar_RegisterVariable (&r_viewmodel_quake);