]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Viewmodels: support cl_bobmodel, cl_leanmodel, cl_followmodel
authorTimePath <andrew.hardaker1995@gmail.com>
Sun, 29 Nov 2015 10:11:49 +0000 (21:11 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Sun, 29 Nov 2015 10:11:49 +0000 (21:11 +1100)
qcsrc/client/view.qc
qcsrc/lib/vector.qh

index 6fc6bbfa25c8914eb1b7fff6c544d718a10bc8f2..5497d2ab958ecac96afee3ba0b6fc01bacd3e308 100644 (file)
 
 #define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOSHADOW | EF_SELECTABLE | EF_TELEPORT_BIT)
 
+float autocvar_cl_viewmodel_scale;
+
+bool autocvar_cl_bobmodel;
+float autocvar_cl_bobmodel_speed;
+float autocvar_cl_bobmodel_side;
+float autocvar_cl_bobmodel_up;
+
+float autocvar_cl_followmodel;
+float autocvar_cl_followmodel_side_speed;
+float autocvar_cl_followmodel_side_highpass;
+float autocvar_cl_followmodel_side_highpass1;
+float autocvar_cl_followmodel_side_limit;
+float autocvar_cl_followmodel_side_lowpass;
+float autocvar_cl_followmodel_up_speed;
+float autocvar_cl_followmodel_up_highpass;
+float autocvar_cl_followmodel_up_highpass1;
+float autocvar_cl_followmodel_up_limit;
+float autocvar_cl_followmodel_up_lowpass;
+
+float autocvar_cl_leanmodel;
+float autocvar_cl_leanmodel_side_speed;
+float autocvar_cl_leanmodel_side_highpass;
+float autocvar_cl_leanmodel_side_highpass1;
+float autocvar_cl_leanmodel_side_lowpass;
+float autocvar_cl_leanmodel_side_limit;
+float autocvar_cl_leanmodel_up_speed;
+float autocvar_cl_leanmodel_up_highpass;
+float autocvar_cl_leanmodel_up_highpass1;
+float autocvar_cl_leanmodel_up_lowpass;
+float autocvar_cl_leanmodel_up_limit;
+
+#define lowpass(value, frac, ref_store, ret) do \
+{ \
+       float __frac = bound(0, frac, 1); \
+       ret = ref_store = ref_store * (1 - __frac) + (value) * __frac; \
+} while (0)
+
+#define lowpass_limited(value, frac, limit, ref_store, ret) do \
+{ \
+       float __ignore; lowpass(value, frac, ref_store, __ignore); \
+       ret = ref_store = bound((value) - (limit), ref_store, (value) + (limit)); \
+} while (0)
+
+#define highpass(value, frac, ref_store, ret) do \
+{ \
+       float __f; lowpass(value, frac, ref_store, __f); \
+       ret = (value) - __f; \
+} while (0)
+
+#define highpass_limited(value, frac, limit, ref_store, ret) do \
+{ \
+       float __f; lowpass_limited(value, frac, limit, ref_store, __f); \
+       ret = (value) - __f; \
+} while (0)
+
+#define lowpass3(value, fracx, fracy, fracz, ref_store, ref_out) do \
+{ \
+       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); \
+} while (0)
+
+#define highpass3(value, fracx, fracy, fracz, ref_store, ref_out) do \
+{ \
+       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); \
+} while (0)
+
+#define highpass3_limited(value, fracx, limitx, fracy, limity, fracz, limitz, ref_store, ref_out) do \
+{ \
+       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); \
+} while (0)
+
+void viewmodel_animate(entity this)
+{
+       static float prevtime;
+       float frametime = (time - prevtime) * getstatf(STAT_MOVEVARS_TIMESCALE);
+       prevtime = time;
+
+       if (autocvar_chase_active) return;
+       if (getstati(STAT_HEALTH) <= 0) return;
+
+       vector gunorg = '0 0 0', gunangles = '0 0 0';
+       static vector gunorg_prev = '0 0 0', gunangles_prev = '0 0 0';
+
+       bool teleported = false; // TODO: detect
+
+       // 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
+               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 * autocvar_cl_followmodel_side_highpass1, autocvar_cl_followmodel_side_limit,
+               frametime * autocvar_cl_followmodel_side_highpass1, autocvar_cl_followmodel_side_limit,
+               frametime * autocvar_cl_followmodel_up_highpass1, autocvar_cl_followmodel_up_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 * autocvar_cl_leanmodel_up_highpass1, autocvar_cl_leanmodel_up_limit,
+               frametime * autocvar_cl_leanmodel_side_highpass1, autocvar_cl_leanmodel_side_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_side_speed : 0);
+       gunorg.y *= (autocvar_cl_followmodel ? -autocvar_cl_followmodel_side_speed : 0);
+       gunorg.z *= (autocvar_cl_followmodel ? -autocvar_cl_followmodel_up_speed : 0);
+
+       PITCH(gunangles) *= (autocvar_cl_leanmodel ? -autocvar_cl_leanmodel_up_speed : 0);
+       YAW(gunangles) *= (autocvar_cl_leanmodel ? -autocvar_cl_leanmodel_side_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 * autocvar_cl_followmodel_side_highpass,
+               frametime * autocvar_cl_followmodel_side_highpass,
+               frametime * autocvar_cl_followmodel_up_highpass,
+               gunorg_adjustment_highpass, gunorg);
+       lowpass3(gunorg,
+               frametime * autocvar_cl_followmodel_side_lowpass,
+               frametime * autocvar_cl_followmodel_side_lowpass,
+               frametime * autocvar_cl_followmodel_up_lowpass,
+               gunorg_adjustment_lowpass, gunorg);
+       // we assume here: PITCH = 0, YAW = 1, ROLL = 2
+       highpass3(gunangles,
+               frametime * autocvar_cl_leanmodel_up_highpass,
+               frametime * autocvar_cl_leanmodel_side_highpass,
+               0,
+               gunangles_adjustment_highpass, gunangles);
+       lowpass3(gunangles,
+               frametime * autocvar_cl_leanmodel_up_lowpass,
+               frametime * autocvar_cl_leanmodel_side_lowpass,
+               0,
+               gunangles_adjustment_lowpass, gunangles);
+       float xyspeed = bound(0, vlen(vec2(csqcplayer.velocity)), 400);
+
+       // vertical view bobbing code
+       // TODO: cl_bob
+
+       // horizontal view bobbing code
+       // TODO: cl_bob2
+
+       // fall bobbing code
+       // causes the view to swing down and back up when touching the ground
+       // TODO: cl_bobfall
+
+       // gun model bobbing code
+       if (autocvar_cl_bobmodel)
+       {
+               // calculate for swinging gun model
+               // the gun bobs when running on the ground, but doesn't bob when you're in the air.
+               // 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, up;
+               float bspeed;
+               float t = 1;
+               float s = time * autocvar_cl_bobmodel_speed;
+               // TODO
+//             if (clonground)
+//             {
+//                     if (time - hitgroundtime < 0.2)
+//                     {
+//                             // just hit the ground, speed the bob back up over the next 0.2 seconds
+//                             t = time - hitgroundtime;
+//                             t = bound(0, t, 0.2);
+//                             t *= 5;
+//                     }
+//             }
+//             else
+//             {
+//                     // recently left the ground, slow the bob down over the next 0.2 seconds
+//                     t = time - lastongroundtime;
+//                     t = 0.2 - bound(0, t, 0.2);
+//                     t *= 5;
+//             }
+               bspeed = xyspeed * 0.01;
+               MAKEVECTORS(makevectors, gunangles, 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;
+       }
+       this.origin += view_forward * gunorg.x + view_right * gunorg.y + view_up * gunorg.z;
+       gunangles.x = -gunangles.x; // pitch was inverted, now that actually matters
+       this.angles += gunangles;
+}
+
+.vector viewmodel_origin, viewmodel_angles;
+
 void viewmodel_draw(entity this)
 {
        int mask = (intermission || (getstati(STAT_HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL;
@@ -58,6 +277,8 @@ void viewmodel_draw(entity this)
                {
                        name_last = name;
                        CL_WeaponEntity_SetModel(this, name);
+                       this.viewmodel_origin = this.origin;
+                       this.viewmodel_angles = this.angles;
                }
                anim_update(this);
                if (!this.animstate_override)
@@ -72,7 +293,6 @@ void viewmodel_draw(entity this)
                        // entity newwep = Weapons_from(activeweapon);
                        float delay = 0.2; // TODO: newwep.switchdelay_raise;
                        f = eta / max(eta, delay);
-                       this.angles_x = -90 * f * f;
                        break;
                }
                case WS_DROP:
@@ -80,7 +300,6 @@ void viewmodel_draw(entity this)
                        // entity oldwep = Weapons_from(activeweapon);
                        float delay = 0.2; // TODO: newwep.switchdelay_drop;
                        f = 1 - eta / max(eta, delay);
-                       this.angles_x = -90 * f * f;
                        break;
                }
                case WS_CLEAR:
@@ -89,7 +308,11 @@ void viewmodel_draw(entity this)
                        break;
                }
        }
-       this.angles_x = -90 * f * f;
+       this.origin = this.viewmodel_origin;
+       this.angles = this.viewmodel_angles;
+       this.angles_x = (-90 * f * f);
+       viewmodel_animate(this);
+       setorigin(this, this.origin);
 }
 
 entity viewmodel;
index 6cbaebdcdb124aa7e18626b084902c8834394749..9813f2fa92d54b9ef2ccb9c1465e2d1e4c0eef3b 100644 (file)
@@ -47,6 +47,16 @@ float boxesoverlap(vector m1, vector m2, vector m3, vector m4) { return m2_x >=
 /** requires the same as boxesoverlap, but is a stronger condition */
 float boxinsidebox(vector smins, vector smaxs, vector bmins, vector bmaxs) { return smins.x >= bmins.x && smaxs.x <= bmaxs.x && smins.y >= bmins.y && smaxs.y <= bmaxs.y && smins.z >= bmins.z && smaxs.z <= bmaxs.z; }
 
+#define PITCH(v) (v).x
+#define YAW(v) (v).y
+#define ROLL(v) (v).z
+
+#define MAKEVECTORS(f, angles, forward, right, up) do { \
+       f(angles); \
+       forward = v_forward; \
+       right = v_right; \
+       up = v_up; \
+} while (0)
 
 vector vec2(vector v)
 {