]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - view.c
Fix initialization of gunorg/vieworg when cl_followmodel/cl_leanmodel are disabled.
[xonotic/darkplaces.git] / view.c
diff --git a/view.c b/view.c
index e5bdad5999b59d828bc91164052f1efc2b43ba1f..1102effb30463628974b4503f832bd6f4e212e37 100644 (file)
--- a/view.c
+++ b/view.c
@@ -48,6 +48,10 @@ cvar_t cl_bobmodel = {CVAR_SAVE, "cl_bobmodel", "1", "enables gun bobbing"};
 cvar_t cl_bobmodel_side = {CVAR_SAVE, "cl_bobmodel_side", "0.15", "gun bobbing sideways sway amount"};
 cvar_t cl_bobmodel_up = {CVAR_SAVE, "cl_bobmodel_up", "0.06", "gun bobbing upward movement amount"};
 cvar_t cl_bobmodel_speed = {CVAR_SAVE, "cl_bobmodel_speed", "7", "gun bobbing speed"};
+cvar_t cl_bob_limit = {CVAR_SAVE, "cl_bob_limit", "7", "limits bobbing to this much distance from view_ofs"};
+cvar_t cl_bob_limit_heightcheck = {CVAR_SAVE, "cl_bob_limit_heightcheck", "0", "check ceiling and floor height against cl_bob_limit and scale down all view bobbing if could result in camera being in solid"};
+cvar_t cl_bob_limit_heightcheck_dontcrosswatersurface = {CVAR_SAVE, "cl_bob_limit_heightcheck_dontcrosswatersurface", "1", "limit cl_bob_limit to not crossing liquid surfaces also"};
+cvar_t cl_bob_velocity_limit = {CVAR_SAVE, "cl_bob_velocity_limit", "400", "limits the xyspeed value in the bobbing code"};
 
 cvar_t cl_leanmodel = {CVAR_SAVE, "cl_leanmodel", "0", "enables gun leaning"};
 cvar_t cl_leanmodel_side_speed = {CVAR_SAVE, "cl_leanmodel_side_speed", "0.7", "gun leaning sideways speed"};
@@ -110,6 +114,8 @@ cvar_t v_deathtiltangle = {0, "v_deathtiltangle", "80", "what roll angle to use
 // Prophecy camera pitchangle by Alexander "motorsep" Zubov
 cvar_t chase_pitchangle = {CVAR_SAVE, "chase_pitchangle", "55", "chase cam pitch angle"};
 
+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)"};
+
 float  v_dmg_time, v_dmg_roll, v_dmg_pitch;
 
 
@@ -120,7 +126,7 @@ V_CalcRoll
 Used by view and sv_user
 ===============
 */
-float V_CalcRoll (vec3_t angles, vec3_t velocity)
+float V_CalcRoll (const vec3_t angles, const vec3_t velocity)
 {
        vec3_t  right;
        float   sign;
@@ -428,7 +434,46 @@ static void highpass3_limited(vec3_t value, vec_t fracx, vec_t limitx, vec_t fra
        out[2] = highpass_limited(value[2], fracz, limitz, &store[2]);
 }
 
-void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qboolean teleported, qboolean clonground, qboolean clcmdjump)
+/*
+ * State:
+ *   cl.bob2_smooth
+ *   cl.bobfall_speed
+ *   cl.bobfall_swing
+ *   cl.gunangles_adjustment_highpass
+ *   cl.gunangles_adjustment_lowpass
+ *   cl.gunangles_highpass
+ *   cl.gunangles_prev
+ *   cl.gunorg_adjustment_highpass
+ *   cl.gunorg_adjustment_lowpass
+ *   cl.gunorg_highpass
+ *   cl.gunorg_prev
+ *   cl.hitgroundtime
+ *   cl.lastongroundtime
+ *   cl.oldongrounbd
+ *   cl.stairsmoothtime
+ *   cl.stairsmoothz
+ *   cl.calcrefdef_prevtime
+ * Extra input:
+ *   cl.movecmd[0].time
+ *   cl.movevars_stepheight
+ *   cl.movevars_timescale
+ *   cl.oldtime
+ *   cl.punchangle
+ *   cl.punchvector
+ *   cl.qw_intermission_angles
+ *   cl.qw_intermission_origin
+ *   cl.qw_weaponkick
+ *   cls.protocol
+ *   cl.time
+ * Output:
+ *   cl.csqc_viewanglesfromengine
+ *   cl.csqc_viewmodelmatrixfromengine
+ *   cl.csqc_vieworiginfromengine
+ *   r_refdef.view.matrix
+ *   viewmodelmatrix_nobob
+ *   viewmodelmatrix_withbob
+ */
+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)
 {
        float vieworg[3], viewangles[3], smoothtime;
        float gunorg[3], gunangles[3];
@@ -452,7 +497,9 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                cl.lastongroundtime = cl.movecmd[0].time;
        }
        cl.oldonground = clonground;
+       cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime);
 
+       VectorClear(gunangles);
        VectorClear(gunorg);
        viewmodelmatrix_nobob = identitymatrix;
        viewmodelmatrix_withbob = identitymatrix;
@@ -471,7 +518,7 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
        if (v_dmg_time > 0)
                v_dmg_time -= bound(0, smoothtime, 0.1);
 
-       if (cl.intermission)
+       if (clintermission)
        {
                // entity is a fixed camera, just copy the matrix
                if (cls.protocol == PROTOCOL_QUAKEWORLD)
@@ -479,11 +526,19 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                else
                {
                        r_refdef.view.matrix = *entrendermatrix;
-                       Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, cl.stats[STAT_VIEWHEIGHT]);
+                       Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight);
                }
+               if (v_yshearing.value > 0)
+                       Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
                Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
                Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
                Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob);
+
+               VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
+               VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
+
+               Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
+               Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value);
        }
        else
        {
@@ -504,8 +559,8 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
 
                // apply the viewofs (even if chasecam is used)
                // Samual: Lets add smoothing for this too so that things like crouching are done with a transition.
-               viewheight = bound(0, (cl.time - cl.oldtime) / max(0.0001, cl_smoothviewheight.value), 1);
-               viewheightavg = viewheightavg * (1 - viewheight) + cl.stats[STAT_VIEWHEIGHT] * viewheight;
+               viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1);
+               viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight;
                vieworg[2] += viewheightavg;
 
                if (chase_active.value)
@@ -534,20 +589,20 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
 #if 0
 #if 1
-                               //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
-                               trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
+                               //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
+                               trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
 #else
-                               //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
-                               trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
+                               //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
+                               trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
 #endif
                                VectorCopy(trace.endpos, vieworg);
                                vieworg[2] -= 8;
 #else
                                // trace from first person view location to our chosen third person view location
 #if 1
-                               trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true);
+                               trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, collision_extendmovelength.value, true, false, NULL, false, true);
 #else
-                               trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
+                               trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, collision_extendmovelength.value, true, false, NULL, false);
 #endif
                                VectorCopy(trace.endpos, bestvieworg);
                                offset[2] = 0;
@@ -560,9 +615,9 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                                chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
                                                chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
 #if 1
-                                               trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true);
+                                               trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, collision_extendmovelength.value, true, false, NULL, false, true);
 #else
-                                               trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
+                                               trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, collision_extendmovelength.value, true, false, NULL, false);
 #endif
                                                if (bestvieworg[2] > trace.endpos[2])
                                                        bestvieworg[2] = trace.endpos[2];
@@ -588,7 +643,7 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                chase_dest[0] = vieworg[0] + forward[0] * dist;
                                chase_dest[1] = vieworg[1] + forward[1] * dist;
                                chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
-                               trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true);
+                               trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, 0, collision_extendmovelength.value, true, false, NULL, false, true);
                                VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
                        }
                }
@@ -596,10 +651,10 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                {
                        // first person view from entity
                        // angles
-                       if (cl.stats[STAT_HEALTH] <= 0 && v_deathtilt.integer)
+                       if (cldead && v_deathtilt.integer)
                                viewangles[ROLL] = v_deathtiltangle.value;
                        VectorAdd(viewangles, cl.punchangle, viewangles);
-                       viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
+                       viewangles[ROLL] += V_CalcRoll(clviewangles, clvelocity);
                        if (v_dmg_time > 0)
                        {
                                viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
@@ -607,66 +662,99 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                        }
                        // origin
                        VectorAdd(vieworg, cl.punchvector, vieworg);
-                       if (cl.stats[STAT_HEALTH] > 0)
+                       if (!cldead)
                        {
                                double xyspeed, bob, bobfall;
-                               float cycle;
+                               double cycle; // double-precision because cl.time can be a very large number, where float would get stuttery at high time values
                                vec_t frametime;
 
-                               //frametime = cl.realframetime * cl.movevars_timescale;
-                               frametime = (cl.time - cl.oldtime) * cl.movevars_timescale;
+                               frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale;
 
-                               // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
-                               if(teleported)
+                               if(cl_followmodel.integer || cl_leanmodel.integer)
                                {
-                                       // try to fix the first highpass; result is NOT
-                                       // perfect! TODO find a better fix
-                                       VectorCopy(viewangles, cl.gunangles_prev);
+                                       // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
+                                       if(teleported)
+                                       {
+                                               // try to fix the first highpass; result is NOT
+                                               // perfect! TODO find a better fix
+                                               VectorCopy(viewangles, cl.gunangles_prev);
+                                               VectorCopy(vieworg, cl.gunorg_prev);
+                                       }
+
+                                       // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
+                                       VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
+                                       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);
                                        VectorCopy(vieworg, cl.gunorg_prev);
+                                       VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
+
+                                       // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
+                                       VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
+                                       cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5);
+                                       cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5);
+                                       cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5);
+                                       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);
+                                       VectorCopy(viewangles, cl.gunangles_prev);
+                                       VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
+
+                                       // 3. calculate the RAW adjustment vectors
+                                       gunorg[0] *= -cl_followmodel_side_speed.value;
+                                       gunorg[1] *= -cl_followmodel_side_speed.value;
+                                       gunorg[2] *= -cl_followmodel_up_speed.value;
+
+                                       gunangles[PITCH] *= -cl_leanmodel_up_speed.value;
+                                       gunangles[YAW] *= -cl_leanmodel_side_speed.value;
+                                       gunangles[ROLL] = 0;
+
+                                       // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
+                                       //    trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
+                                       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);
+                                       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);
+                                       // we assume here: PITCH = 0, YAW = 1, ROLL = 2
+                                       highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles);
+                                       lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles);
+
+                                       // 5. use the adjusted vectors
+                                       VectorAdd(vieworg, gunorg, gunorg);
+                                       VectorAdd(viewangles, gunangles, gunangles);
+                               }
+                               else
+                               {
+                                       // Just initialize gunorg/gunangles.
+                                       VectorCopy(vieworg, gunorg);
+                                       VectorCopy(viewangles, gunangles);
                                }
-
-                               // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
-                               VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
-                               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);
-                               VectorCopy(vieworg, cl.gunorg_prev);
-                               VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
-
-                               // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
-                               VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
-                               cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5);
-                               cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5);
-                               cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5);
-                               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);
-                               VectorCopy(viewangles, cl.gunangles_prev);
-                               VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
-
-                               // 3. calculate the RAW adjustment vectors
-                               gunorg[0] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0);
-                               gunorg[1] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0);
-                               gunorg[2] *= (cl_followmodel.value ? -cl_followmodel_up_speed.value : 0);
-
-                               gunangles[PITCH] *= (cl_leanmodel.value ? -cl_leanmodel_up_speed.value : 0);
-                               gunangles[YAW] *= (cl_leanmodel.value ? -cl_leanmodel_side_speed.value : 0);
-                               gunangles[ROLL] = 0;
-
-                               // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
-                               //    trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
-                               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);
-                               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);
-                               // we assume here: PITCH = 0, YAW = 1, ROLL = 2
-                               highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles);
-                               lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles);
-
-                               // 5. use the adjusted vectors
-                               VectorAdd(vieworg, gunorg, gunorg);
-                               VectorAdd(viewangles, gunangles, gunangles);
 
                                // bounded XY speed, used by several effects below
-                               xyspeed = bound (0, sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]), 400);
+                               xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), cl_bob_velocity_limit.value);
 
                                // vertical view bobbing code
                                if (cl_bob.value && cl_bobcycle.value)
                                {
+                                       float bob_limit = cl_bob_limit.value;
+
+                                       if (cl_bob_limit_heightcheck.integer)
+                                       {
+                                               // use traces to determine what range the view can bob in, and scale down the bob as needed
+                                               float trace1fraction;
+                                               float trace2fraction;
+                                               vec3_t bob_height_check_dest;
+
+                                               // 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
+                                               bob_height_check_dest[0] = vieworg[0];
+                                               bob_height_check_dest[1] = vieworg[1];
+                                               bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * 1.1f;
+                                               trace = CL_TraceLine(vieworg, bob_height_check_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY | (cl_bob_limit_heightcheck_dontcrosswatersurface.integer ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, collision_extendmovelength.value, true, false, NULL, false, true);
+                                               trace1fraction = trace.fraction;
+
+                                               bob_height_check_dest[0] = vieworg[0];
+                                               bob_height_check_dest[1] = vieworg[1];
+                                               bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * -0.5f;
+                                               trace = CL_TraceLine(vieworg, bob_height_check_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY | (cl_bob_limit_heightcheck_dontcrosswatersurface.integer ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, collision_extendmovelength.value, true, false, NULL, false, true);
+                                               trace2fraction = trace.fraction;
+
+                                               bob_limit *= min(trace1fraction, trace2fraction);
+                                       }
+
                                        // LordHavoc: this code is *weird*, but not replacable (I think it
                                        // should be done in QC on the server, but oh well, quake is quake)
                                        // LordHavoc: figured out bobup: the time at which the sin is at 180
@@ -679,7 +767,8 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                                cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
                                        // bob is proportional to velocity in the xy plane
                                        // (don't count Z, or jumping messes it up)
-                                       bob = xyspeed * bound(0, cl_bob.value, 0.05);
+                                       bob = xyspeed * cl_bob.value;
+                                       bob = bound(0, bob, bob_limit);
                                        bob = bob*0.3 + bob*0.7*cycle;
                                        vieworg[2] += bob;
                                        // we also need to adjust gunorg, or this appears like pushing the gun!
@@ -702,7 +791,7 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                                cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin
                                        else
                                                cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5);
-                                       bob = bound(0, cl_bob2.value, 0.05) * cycle;
+                                       bob = cl_bob2.value * cycle;
 
                                        // this value slowly decreases from 1 to 0 when we stop touching the ground.
                                        // The cycle is later multiplied with it so the view smooths back to normal
@@ -719,8 +808,8 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                        // calculate the front and side of the player between the X and Y axes
                                        AngleVectors(viewangles, forward, right, up);
                                        // now get the speed based on those angles. The bounds should match the same value as xyspeed's
-                                       side = bound(-400, DotProduct (cl.velocity, right) * cl.bob2_smooth, 400);
-                                       front = bound(-400, DotProduct (cl.velocity, forward) * cl.bob2_smooth, 400);
+                                       side = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, right) * cl.bob2_smooth, cl_bob_velocity_limit.value);
+                                       front = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, forward) * cl.bob2_smooth, cl_bob_velocity_limit.value);
                                        VectorScale(forward, bob, forward);
                                        VectorScale(right, bob, right);
                                        // we use side with forward and front with right, so the bobbing goes
@@ -742,8 +831,8 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                                {
                                        if (!clonground)
                                        {
-                                               cl.bobfall_speed = bound(-400, cl.velocity[2], 0) * bound(0, cl_bobfall.value, 0.1);
-                                               if (cl.velocity[2] < -cl_bobfallminspeed.value)
+                                               cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1);
+                                               if (clvelocity[2] < -cl_bobfallminspeed.value)
                                                        cl.bobfall_swing = 1;
                                                else
                                                        cl.bobfall_swing = 0; // TODO really?
@@ -809,30 +898,39 @@ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewa
                        viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
                }
                Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1);
+               if (v_yshearing.value > 0)
+                       Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
 
                // calculate a viewmodel matrix for use in view-attached entities
                Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
                Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
 
                Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
+               if (v_yshearing.value > 0)
+                       Matrix4x4_QuakeToDuke3D(&viewmodelmatrix_withbob, &viewmodelmatrix_withbob, v_yshearing.value);
+
                VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
                VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
 
                Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
                Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob);
        }
+
+       cl.calcrefdef_prevtime = cl.time;
 }
 
 void V_CalcRefdef (void)
 {
        entity_t *ent;
+       qboolean cldead;
 
-       if (cls.state == ca_connected && cls.signon == SIGNONS && !cl.csqc_server2csqcentitynumber[cl.playerentity])
+       if (cls.state == ca_connected && cls.signon == SIGNONS && !cl.csqc_server2csqcentitynumber[cl.viewentity])
        {
                // ent is the view entity (visible when out of body)
                ent = &cl.entities[cl.viewentity];
 
-               V_CalcRefdefUsing(&ent->render.matrix, cl.viewangles, !ent->persistent.trail_allowed, cl.onground, cl.cmd.jump); // FIXME use a better way to detect teleport/warp than trail_allowed
+               cldead = (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && cl.stats[STAT_HEALTH] != -2342);
+               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
        }
        else
        {
@@ -1045,6 +1143,10 @@ void V_Init (void)
        Cvar_RegisterVariable (&cl_bobmodel_side);
        Cvar_RegisterVariable (&cl_bobmodel_up);
        Cvar_RegisterVariable (&cl_bobmodel_speed);
+       Cvar_RegisterVariable (&cl_bob_limit);
+       Cvar_RegisterVariable (&cl_bob_limit_heightcheck);
+       Cvar_RegisterVariable (&cl_bob_limit_heightcheck_dontcrosswatersurface);
+       Cvar_RegisterVariable (&cl_bob_velocity_limit);
 
        Cvar_RegisterVariable (&cl_leanmodel);
        Cvar_RegisterVariable (&cl_leanmodel_side_speed);
@@ -1089,5 +1191,7 @@ void V_Init (void)
 
        Cvar_RegisterVariable (&v_deathtilt);
        Cvar_RegisterVariable (&v_deathtiltangle);
+
+       Cvar_RegisterVariable (&v_yshearing);
 }