]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/client/view.qc
Fix weaponmodel effects passing through warpzones
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / view.qc
index 6ba4b6f568df99d48b7b05b6426161536512a198..d6d82acbe2bd06a45d3f88a980fdf72578a0f3cc 100644 (file)
@@ -52,10 +52,10 @@ float autocvar_cl_leanmodel_highpass1 = 0.2;
 float autocvar_cl_leanmodel_highpass = 0.2;
 float autocvar_cl_leanmodel_lowpass = 0.05;
 
+#define avg_factor(avg_time) (1 - exp(-frametime / max(0.001, avg_time)))
 #define lowpass(value, frac, ref_store, ret) MACRO_BEGIN \
 { \
-       float __frac = 1 - exp(-frac); \
-       ret = ref_store = ref_store * (1 - __frac) + (value) * __frac; \
+       ret = ref_store = ref_store * (1 - frac) + (value) * frac; \
 } MACRO_END
 
 #define lowpass_limited(value, frac, limit, ref_store, ret) MACRO_BEGIN \
@@ -76,31 +76,62 @@ float autocvar_cl_leanmodel_lowpass = 0.05;
        ret = (value) - __f; \
 } MACRO_END
 
-#define lowpass3(value, fracx, fracy, fracz, ref_store, ref_out) MACRO_BEGIN \
+#define lowpass2(value, frac, ref_store, ref_out) MACRO_BEGIN \
 { \
-       lowpass(value.x, fracx, ref_store.x, ref_out.x); \
-       lowpass(value.y, fracy, ref_store.y, ref_out.y); \
-       lowpass(value.z, fracz, ref_store.z, ref_out.z); \
+       lowpass(value.x, frac, ref_store.x, ref_out.x); \
+       lowpass(value.y, frac, ref_store.y, ref_out.y); \
 } MACRO_END
 
-#define highpass3(value, fracx, fracy, fracz, ref_store, ref_out) MACRO_BEGIN \
+#define lowpass2_limited(value, frac, limit, ref_store, ref_out) MACRO_BEGIN \
 { \
-       highpass(value.x, fracx, ref_store.x, ref_out.x); \
-       highpass(value.y, fracy, ref_store.y, ref_out.y); \
-       highpass(value.z, fracz, ref_store.z, ref_out.z); \
+       lowpass_limited(value.x, frac, limit, ref_store.x, ref_out.x); \
+       lowpass_limited(value.y, frac, limit, ref_store.y, ref_out.y); \
 } MACRO_END
 
-#define highpass3_limited(value, fracx, limitx, fracy, limity, fracz, limitz, ref_store, ref_out) MACRO_BEGIN \
+#define highpass2(value, frac, ref_store, ref_out) MACRO_BEGIN \
 { \
-       highpass_limited(value.x, fracx, limitx, ref_store.x, ref_out.x); \
-       highpass_limited(value.y, fracy, limity, ref_store.y, ref_out.y); \
-       highpass_limited(value.z, fracz, limitz, ref_store.z, ref_out.z); \
+       highpass(value.x, frac, ref_store.x, ref_out.x); \
+       highpass(value.y, frac, ref_store.y, ref_out.y); \
+} MACRO_END
+
+#define highpass2_limited(value, frac, limit, ref_store, ref_out) MACRO_BEGIN \
+{ \
+       highpass_limited(value.x, frac, limit, ref_store.x, ref_out.x); \
+       highpass_limited(value.y, frac, limit, ref_store.y, ref_out.y); \
+} MACRO_END
+
+#define lowpass3(value, frac, ref_store, ref_out) MACRO_BEGIN \
+{ \
+       lowpass(value.x, frac, ref_store.x, ref_out.x); \
+       lowpass(value.y, frac, ref_store.y, ref_out.y); \
+       lowpass(value.z, frac, ref_store.z, ref_out.z); \
+} MACRO_END
+
+#define highpass3(value, frac, ref_store, ref_out) MACRO_BEGIN \
+{ \
+       highpass(value.x, frac, ref_store.x, ref_out.x); \
+       highpass(value.y, frac, ref_store.y, ref_out.y); \
+       highpass(value.z, frac, ref_store.z, ref_out.z); \
+} MACRO_END
+
+#define highpass3_limited(value, frac, limit, ref_store, ref_out) MACRO_BEGIN \
+{ \
+       highpass_limited(value.x, frac, limit, ref_store.x, ref_out.x); \
+       highpass_limited(value.y, frac, limit, ref_store.y, ref_out.y); \
+       highpass_limited(value.z, frac, limit, ref_store.z, ref_out.z); \
+} MACRO_END
+
+#define lowpass3_limited(value, frac, limit, ref_store, ref_out) MACRO_BEGIN \
+{ \
+       lowpass_limited(value.x, frac, limit, ref_store.x, ref_out.x); \
+       lowpass_limited(value.y, frac, limit, ref_store.y, ref_out.y); \
+       lowpass_limited(value.z, frac, limit, ref_store.z, ref_out.z); \
 } MACRO_END
 
 void viewmodel_animate(entity this)
 {
        static float prevtime;
-       float frametime = (time - prevtime) * STAT(MOVEVARS_TIMESCALE);
+       float frametime = (time - prevtime);
        prevtime = time;
 
        if (autocvar_chase_active) return;
@@ -121,84 +152,72 @@ void viewmodel_animate(entity this)
        }
        oldonground = clonground;
 
-       vector gunorg = '0 0 0', gunangles = '0 0 0';
-       static vector gunorg_prev = '0 0 0', gunangles_prev = '0 0 0';
 
        bool teleported = view.csqcmodel_teleported;
 
-       // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
-       if (teleported)
+       float frac;
+       if(autocvar_cl_followmodel)
+       {
+               vector gunorg = '0 0 0';
+               static vector vel_average;
+               static vector gunorg_prev = '0 0 0';
+               static vector gunorg_adjustment_highpass;
+               static vector gunorg_adjustment_lowpass;
+
+               vector vel;
+               vector forward, right = '0 0 0', up = '0 0 0';
+               MAKEVECTORS(makevectors, view_angles, forward, right, up);
+               vel.x = view.velocity * forward;
+               vel.y = view.velocity * right * -1;
+               vel.z = view.velocity * up;
+               frac = avg_factor(autocvar_cl_followmodel_highpass1);
+               lowpass3_limited(vel, frac, autocvar_cl_followmodel_limit, vel_average, gunorg);
+
+               gunorg *= -autocvar_cl_followmodel_speed * 0.042;
+
+               // 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!
+               frac = avg_factor(autocvar_cl_followmodel_highpass);
+               highpass3(gunorg, frac, gunorg_adjustment_highpass, gunorg);
+               frac = avg_factor(autocvar_cl_followmodel_lowpass);
+               lowpass3(gunorg, frac, gunorg_adjustment_lowpass, gunorg);
+
+               this.origin += gunorg;
+       }
+
+       if(autocvar_cl_leanmodel)
        {
-               // try to fix the first highpass; result is NOT
-               // perfect! TODO find a better fix
+               vector gunangles = '0 0 0';
+               static vector gunangles_prev = '0 0 0';
+               static vector gunangles_highpass = '0 0 0';
+               static vector gunangles_adjustment_highpass;
+               static vector gunangles_adjustment_lowpass;
+
+               if (teleported)
+                       gunangles_prev = view_angles;
+
+               // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
+               gunangles_highpass += gunangles_prev;
+               PITCH(gunangles_highpass) += 360 * floor((PITCH(view_angles) - PITCH(gunangles_highpass)) / 360 + 0.5);
+               YAW(gunangles_highpass) += 360 * floor((YAW(view_angles) - YAW(gunangles_highpass)) / 360 + 0.5);
+               ROLL(gunangles_highpass) += 360 * floor((ROLL(view_angles) - ROLL(gunangles_highpass)) / 360 + 0.5);
+               frac = avg_factor(autocvar_cl_leanmodel_highpass1);
+               highpass2_limited(view_angles, frac, autocvar_cl_leanmodel_limit, gunangles_highpass, gunangles);
                gunangles_prev = view_angles;
-               gunorg_prev = view_origin;
-       }
-
-       static vector gunorg_highpass = '0 0 0';
-
-       // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
-       gunorg_highpass += gunorg_prev;
-       highpass3_limited(view_origin,
-               frametime / max(0.001, autocvar_cl_followmodel_highpass1), autocvar_cl_followmodel_limit,
-               frametime / max(0.001, autocvar_cl_followmodel_highpass1), autocvar_cl_followmodel_limit,
-               frametime / max(0.001, autocvar_cl_followmodel_highpass1), autocvar_cl_followmodel_limit,
-               gunorg_highpass, gunorg);
-       gunorg_prev = view_origin;
-       gunorg_highpass -= gunorg_prev;
-
-       static vector gunangles_highpass = '0 0 0';
-
-       // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
-       gunangles_highpass += gunangles_prev;
-       PITCH(gunangles_highpass) += 360 * floor((PITCH(view_angles) - PITCH(gunangles_highpass)) / 360 + 0.5);
-       YAW(gunangles_highpass) += 360 * floor((YAW(view_angles) - YAW(gunangles_highpass)) / 360 + 0.5);
-       ROLL(gunangles_highpass) += 360 * floor((ROLL(view_angles) - ROLL(gunangles_highpass)) / 360 + 0.5);
-       highpass3_limited(view_angles,
-               frametime / max(0.001, autocvar_cl_leanmodel_highpass1), autocvar_cl_leanmodel_limit,
-               frametime / max(0.001, autocvar_cl_leanmodel_highpass1), autocvar_cl_leanmodel_limit,
-               0, 0,
-               gunangles_highpass, gunangles);
-       gunangles_prev = view_angles;
-       gunangles_highpass -= gunangles_prev;
-
-       // 3. calculate the RAW adjustment vectors
-       gunorg.x *= (autocvar_cl_followmodel ? -autocvar_cl_followmodel_speed : 0);
-       gunorg.y *= (autocvar_cl_followmodel ? -autocvar_cl_followmodel_speed : 0);
-       gunorg.z *= (autocvar_cl_followmodel ? -autocvar_cl_followmodel_speed : 0);
-
-       PITCH(gunangles) *= (autocvar_cl_leanmodel ? -autocvar_cl_leanmodel_speed : 0);
-       YAW(gunangles) *= (autocvar_cl_leanmodel ? -autocvar_cl_leanmodel_speed : 0);
-       ROLL(gunangles) = 0;
-
-       static vector gunorg_adjustment_highpass;
-       static vector gunorg_adjustment_lowpass;
-       static vector gunangles_adjustment_highpass;
-       static vector gunangles_adjustment_lowpass;
-
-       // 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 / max(0.001, autocvar_cl_followmodel_highpass),
-               frametime / max(0.001, autocvar_cl_followmodel_highpass),
-               frametime / max(0.001, autocvar_cl_followmodel_highpass),
-               gunorg_adjustment_highpass, gunorg);
-       lowpass3(gunorg,
-               frametime / max(0.001, autocvar_cl_followmodel_lowpass),
-               frametime / max(0.001, autocvar_cl_followmodel_lowpass),
-               frametime / max(0.001, autocvar_cl_followmodel_lowpass),
-               gunorg_adjustment_lowpass, gunorg);
-       // we assume here: PITCH = 0, YAW = 1, ROLL = 2
-       highpass3(gunangles,
-               frametime / max(0.001, autocvar_cl_leanmodel_highpass),
-               frametime / max(0.001, autocvar_cl_leanmodel_highpass),
-               0,
-               gunangles_adjustment_highpass, gunangles);
-       lowpass3(gunangles,
-               frametime / max(0.001, autocvar_cl_leanmodel_lowpass),
-               frametime / max(0.001, autocvar_cl_leanmodel_lowpass),
-               0,
-               gunangles_adjustment_lowpass, gunangles);
+               gunangles_highpass -= gunangles_prev;
+
+               PITCH(gunangles) *= -autocvar_cl_leanmodel_speed;
+               YAW(gunangles) *= -autocvar_cl_leanmodel_speed;
+
+               // we assume here: PITCH = 0, YAW = 1, ROLL = 2
+               frac = avg_factor(autocvar_cl_leanmodel_highpass);
+               highpass2(gunangles, frac, gunangles_adjustment_highpass, gunangles);
+               frac = avg_factor(autocvar_cl_leanmodel_lowpass);
+               lowpass2(gunangles, frac, gunangles_adjustment_lowpass, gunangles);
+
+               gunangles.x = -gunangles.x; // pitch was inverted, now that actually matters
+               this.angles += gunangles;
+       }
 
        float xyspeed = bound(0, vlen(vec2(view.velocity)), 400);
 
@@ -220,7 +239,6 @@ void viewmodel_animate(entity this)
                // Sajt: I tried to smooth out the transitions between bob and no bob, which works
                // for the most part, but for some reason when you go through a message trigger or
                // pick up an item or anything like that it will momentarily jolt the gun.
-               vector forward, right = '0 0 0', up = '0 0 0';
                float bspeed;
                float t = 1;
                float s = time * autocvar_cl_bobmodel_speed;
@@ -242,17 +260,12 @@ void viewmodel_animate(entity this)
                        t *= 5;
                }
                bspeed = xyspeed * 0.01;
-               MAKEVECTORS(makevectors, view_angles, forward, right, up);
-               float bobr = bspeed * autocvar_cl_bobmodel_side * autocvar_cl_viewmodel_scale * sin(s) * t;
-               gunorg += bobr * right;
-               float bobu = bspeed * autocvar_cl_bobmodel_up * autocvar_cl_viewmodel_scale * cos(s * 2) * t;
-               gunorg += bobu * up;
-       }
-       vector v = rotate(gunorg, YAW(view_angles) * DEG2RAD); // rotate world coordinates to relative ones
-       v.z = gunorg.z;
-       this.origin += v;
-       gunangles.x = -gunangles.x; // pitch was inverted, now that actually matters
-       this.angles += gunangles;
+               vector gunorg = '0 0 0';
+               gunorg.y += bspeed * autocvar_cl_bobmodel_side * autocvar_cl_viewmodel_scale * sin(s) * t;
+               gunorg.z += bspeed * autocvar_cl_bobmodel_up * autocvar_cl_viewmodel_scale * cos(s * 2) * t;
+
+               this.origin += gunorg;
+       }
 }
 
 .vector viewmodel_origin, viewmodel_angles;
@@ -262,6 +275,8 @@ void viewmodel_animate(entity this)
 
 void viewmodel_draw(entity this)
 {
+       if(!activeweapon)
+               return;
        int mask = (intermission || (STAT(HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
        float a = this.alpha;
        static bool wasinvehicle;
@@ -1649,6 +1664,10 @@ void CSQC_UpdateView(float w, float h)
                ov_enabled = false;
        }
 
+       // run viewmodel_draw before updating view_angles to the angles calculated by WarpZone_FixView
+       // viewmodel_draw needs to use the view_angles set by the engine on every CSQC_UpdateView call
+       FOREACH_ENTITY(it.draw, LAMBDA(it.draw(it)));
+
        // Render the Scene
        view_origin = getpropertyvec(VF_ORIGIN);
        view_angles = getpropertyvec(VF_ANGLES);
@@ -1829,8 +1848,6 @@ void CSQC_UpdateView(float w, float h)
           mousepos = mousepos*0.5 + getmousepos();
         */
 
-       FOREACH_ENTITY(it.draw, LAMBDA(it.draw(it)));
-
        addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
        renderscene();