djcev.com

//

Git Repos / fte_dogmode / commit 7839d54

Commit: 7839d54d6a272fbbb5b532feb8d9a97b76d3ddf4
Parent: 5ef6e0ec89d8c603c0b78d8ba341ac277ad156f8
Author: Cameron Vanderzanden, 2023-11-08 07:51
Committer: Cameron Vanderzanden, 2023-11-08 07:51

Commit Message

Refinements to NuclideMove, step smoothing change

Refactored & simplified PM_NuclideMove. It should correctly
solve more edge cases now & maybe run a bit faster.

Also changed the minimum stepsize for view smoothing from 8 to 6
in CSQC.

Change List

?File Add Del
M qc/csqc/csqc_player.qc +3 -3
M qc/pmove.qc +358 -89

Diff qc/csqc/csqc_player.qc

diff --git a/qc/csqc/csqc_player.qc b/qc/csqc/csqc_player.qc
index ecadb51..a03259e 100644
--- a/qc/csqc/csqc_player.qc
+++ b/qc/csqc/csqc_player.qc
@@ -115,7 +115,7 @@ float() PlayerPreDraw =
{
PlayerRunMovement (self, clientcommandframe);

- if (self.origin_z >= player_step_oldz + 8 &&
+ if (self.origin_z >= player_step_oldz + 6 &&
self.origin_z < player_step_oldz + 24 &&
self.velocity_z == 0)
{
@@ -246,8 +246,8 @@ void(float isnew) PlayerUpdate =
player_org = o;
player_vel = v;
// TODO CEV ????
- // player_pmflags = self.pmove_flags;
- player_pmflags = pmflags;
+ player_pmflags = self.pmove_flags;
+ // player_pmflags = pmflags;
player_sequence = servercommandframe;
PlayerResetPrediction (self);
}

Return to the top of this page or return to the overview of this repo.

Diff qc/pmove.qc

diff --git a/qc/pmove.qc b/qc/pmove.qc
index c876f44..0dc8b6e 100644
--- a/qc/pmove.qc
+++ b/qc/pmove.qc
@@ -128,27 +128,29 @@ void(vector normal, float ob, float oneside) PM_Rebound =
// PM_NuclideMove
//
// Updates origin, moves the player through the world. Similar to Q3
-// SlideMove and Q1 FlyMove. This version applies gravity and restores
-// velocity based on a timer check (both features of Quake 3's pmove).
+// SlideMove and Q1 FlyMove. This version handles steps, applies gravity,
+// and restores velocity based on a timer check (those latter two are
+// features of Q3's SlideMove).
//
// Based on code from the Nuclide SDK (presumably by Eukara), specifically
// the function PMoveCustom_Move found in the file pmove_custom.qc.
// This in turn appears to be based on a similar function in CSQCTest.
+// -- CEV
//----------------------------------------------------------------------
-#define PM_SLIDE_NUMBUMPS 5
void(float dogravity, float onesided) PM_NuclideMove =
{
- vector end, previous_plane, saved_plane, start_o, start_v;
- float dostep, ent_grav, grav, stepsize, time_left;
+ vector end, prev_plane, plane, start_o, start_v;
+ float backoff, dostep, grav, stepsz, time_left;
int i, start_onground;
+ entity ent;

if (dogravity)
{
if (self.gravity)
- ent_grav = self.gravity;
+ grav = self.gravity;
else
- ent_grav = 1.0;
- grav = ent_grav * autocvar(sv_gravity, 800) * input_timelength;
+ grav = 1.0;
+ grav = grav * autocvar (sv_gravity, 800) * input_timelength;
// Half now, half later. Apparently affects framerate
// dependence. -- CEV
self.velocity_z -= grav * 0.5;
@@ -169,11 +171,10 @@ void(float dogravity, float onesided) PM_NuclideMove =
dostep = FALSE;

// we need to bounce off surfaces (in order to slide along them),
- // so we need at 2 attempts -- comment from Nuclide
+ // so we need at 2 attempts -- comment from Nuclide SDK
// I've changed this to a larger number of attempts (from 3 to 5)
// to accommodate more complex plane interactions -- CEV
- i = PM_SLIDE_NUMBUMPS;
- for (time_left = input_timelength; time_left > 0 && i; i--)
+ for (i = 5, time_left = input_timelength; time_left > 0 && i; i--)
{
end = self.origin + (self.velocity * time_left);
tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);
@@ -199,96 +200,142 @@ void(float dogravity, float onesided) PM_NuclideMove =
// made the whole move -- CEV
break;

- saved_plane = trace_plane_normal;
+ stepsz = 0;
+ plane = trace_plane_normal;
+ ent = trace_ent;
time_left -= time_left * trace_fraction;

// integrated StepSlideMove -- CEV
- // only attempt to step if there's time left, dostep is
- // true, and we encountered something like a vertical plane
- if (time_left && dostep && saved_plane_z >= 0 &&
- saved_plane_z <= 0.7f)
+ // only attempt to step if there's time left, stepping was
+ // requested, and we hit something like a vertical plane
+ if (dostep && time_left && plane_z >= 0 && plane_z <= 0.7f)
{
// first: move up
trace_endpos = self.origin;
trace_endpos_z += PM_STEPHEIGHT;
tracebox (self.origin, self.mins, self.maxs,
trace_endpos, FALSE, self);
- stepsize = trace_endpos_z - self.origin_z;
-
- // second: move forward
- end = trace_endpos + (self.velocity * time_left);
- end_z = trace_endpos_z;
- tracebox (trace_endpos, self.mins, self.maxs,
- end, FALSE, self);

if (trace_fraction >= 1.0f)
{
- // forward move did not hit anything
- local float fwd_fraction = trace_fraction;
- local vector fwd_plane = trace_plane_normal;
-
- // third: move down
- end = trace_endpos;
- end_z -= stepsize;
+ // up move didn't hit anything
+ // second: move forward
+ stepsz = trace_endpos_z - self.origin_z;
+ end = trace_endpos +
+ (self.velocity * time_left);
+ end_z = trace_endpos_z;
tracebox (trace_endpos, self.mins, self.maxs,
end, FALSE, self);

- if (trace_fraction < 1.0f &&
- trace_plane_normal_z > 0.7f)
+ if (trace_fraction >= 1.0f)
{
- // "good ground", accept the step move
- self.origin = trace_endpos;
+ // forward move didn't hit anything
+ // third: move down
+ end = trace_endpos;
+ end_z -= stepsz;
+ tracebox (trace_endpos, self.mins,
+ self.maxs, end, FALSE, self);
+
+ if (trace_fraction < 1.0f &&
+ trace_plane_normal_z > 0.7f)
+ {
+ // good ground, accept the move
+ // laugh at my 80 column term
+ stepsz = trace_endpos_z -
+ self.origin_z;
+ self.origin = trace_endpos;
+ time_left -= time_left *
+ trace_fraction;
+ plane = trace_plane_normal;
+ ent = trace_ent;
+ // don't attempt to step again
+ dostep = FALSE;
+ }
+ else
+ {
+ // down move didn't hit and/or
+ // it hit a plane too steep to
+ // be ground -- CEV
+ stepsz = 0;
+ }
+ }
+ else if (trace_plane_normal_z == 0)
+ {
+ // raised forward move hit something
+ // so clip to it. solves issues with
+ // complex movement on stairs. note
+ // that we're not updating origin.
+ // -- CEV
+ stepsz = 0;
time_left -= time_left * trace_fraction;
- saved_plane = trace_plane_normal;
+ plane = trace_plane_normal;
+ ent = trace_ent;
}
}
- else if (trace_plane_normal_z == 0)
+ else
{
- // forward move hit something, so clip to
- // it. solves issues with complex movement
- // on stairs -- CEV
- time_left -= time_left * trace_fraction;
- saved_plane = trace_plane_normal;
+ // up move hit something (the roof?)
+ // ignore the step attempt entirely -- CEV
+ stepsz = 0;
}
}

// Slide along the crease of previous plane and current plane
// if previous plane is not the same as current plane AND
- // our velocity interacts with the previous plane OR
- // our velocity interacts with the current plane AND
- // current plane is an acute vertical angle -- CEV
+ // our velocity interacts with the current or previous plane
+ // if those planes are an acute vertical angle -- CEV
//
- // TODO CEV: this is *definitely* wrong, but solves some
+ // TODO CEV: this is *definitely* wrong, but it solves some
// immediate problems
- if (previous_plane && previous_plane != saved_plane &&
- (!(self.velocity * previous_plane >= 0) ||
- (!(self.velocity * saved_plane >= 0) &&
- saved_plane_z < 0)))
+ if (prev_plane && prev_plane != plane &&
+ (
+ (!(self.velocity * prev_plane >= 0) &&
+ prev_plane_z < 0) ||
+ (!(self.velocity * plane >= 0) &&
+ plane_z < 0)
+ ))
{
- #ifdef SSQC
- dprint ("PM_NuclideMove: sliding along a crease...\n");
- #endif
- end = crossproduct (previous_plane, saved_plane);
+ end = crossproduct (prev_plane, plane);
end = normalize (end);
self.velocity = end * (end * self.velocity);
}

- // clip velocity to the plane from before (saved_plane)
- // then touch trace_ent. This may have tracking problems
- // (trace_ent might not correspond to saved_plane) -- CEV
- PM_Rebound (saved_plane, PM_OVERCLIP, onesided);
- PM_DoTouch (trace_ent);
- previous_plane = saved_plane;
- }
+ // clip velocity to the saved plane
+ // integrated PM_Rebound aka PM_ClipVelocity -- CEV
+ backoff = self.velocity * plane;

- // if we hit the bump limit, zero out velocity to prevent
- // oscillations in movement (especially in corners) -- CEV
- if (i == 0)
- {
- #ifdef SSQC
- dprint ("PM_NuclideMove: hit the bump limit\n");
- #endif
- self.velocity = '0 0 0';
+ if (backoff < 0)
+ backoff *= PM_OVERCLIP;
+ else
+ if (onesided)
+ backoff *= -0.001;
+ else
+ backoff /= PM_OVERCLIP;
+
+ self.velocity -= plane * backoff;
+
+ // integrated ground check -- CEV
+ if (plane_z > 0.7)
+ {
+ if (ent.solid == SOLID_BSP)
+ {
+ self.groundentity = ent;
+ self.groundnormal = plane;
+ self.pmove_flags |= PMF_ONGROUND;
+ }
+ }
+ else
+ {
+ self.groundentity = __NULL__;
+ self.groundnormal = __NULL__;
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+
+ // touch the saved entity -- CEV
+ PM_DoTouch (ent);
+
+ // store plane for the crease check above
+ prev_plane = plane;
}

// wallclip / wall skim timer check
@@ -304,7 +351,8 @@ void(float dogravity, float onesided) PM_NuclideMove =
#ifdef SSQC
dprint ("PM_NuclideMove: wallclip, restoring velocity...\n");
#endif
- // take whichever Z velocity is lower; fixes steep ramps -- CEV
+ // restore velocity, taking either the start Z or clipped Z
+ // depending on which is lower -- CEV
if (start_v_z > self.velocity_z)
self.velocity = [start_v_x, start_v_y, self.velocity_z];
else
@@ -316,27 +364,17 @@ void(float dogravity, float onesided) PM_NuclideMove =
self.velocity_z -= grav * 0.5;

// manage the "boost" timer -- CEV
- // this seems like an overly complicated check -- CEV
- if (!start_onground && (self.pmove_flags & PMF_ONGROUND) &&
- (start_v_z > self.velocity_z) &&
- (self.velocity_z < 1) &&
- (self.origin_z - start_o_z > 0))
+ if (!start_onground && self.pmove_flags & PMF_ONGROUND && stepsz)
{
if (!self.boost_time &&
self.boost_time <= time - PM_BOOST_WINDOW)
self.boost_time = time;
- /*
- if (cvar("developer"))
- {
- // more debugging -- CEV
- #ifdef SSQC
- local float stepsize2;
- stepsize2 = self.origin_z - start_o_z;
- dprint (sprintf("PM_NuclideMove: step up: %g, %g\n",
- stepsize, stepsize2));
- #endif
- }
- */
+
+ // more debugging -- CEV
+ #ifdef SSQC
+ dprint (sprintf("PM_NuclideMove: airstep: %g, %g\n", stepsz,
+ self.origin_z - start_o_z));
+ #endif
}

/*
@@ -951,6 +989,9 @@ void(entity target) PM_Move =
(self.pmove_flags & PMF_WALLJUMP))
onesided = TRUE;

+ if (self.boost_time >= (time - PM_BOOST_WINDOW))
+ onesided = TRUE;
+
// apply gravity when in the air
dogravity = !(self.pmove_flags & PMF_ONGROUND);

@@ -982,9 +1023,9 @@ void(entity target) PM_Move =
self = oldself;
};

-//----------------------------------------------------------------------
+//------------------------------------------------------------------------------
// Code graveyard below. Saved here for reference purposes. -- CEV
-//----------------------------------------------------------------------
+//------------------------------------------------------------------------------

//======================================================================
// PM_ClipVelocity
@@ -1019,6 +1060,234 @@ vector(vector vel, vector normal, float ob, float oneside) PM_ClipVelocity =
#endif

//======================================================================
+// PM_Q3SlideMove
+//
+// Based on Quake 3's PM_SlideMove (found in the Quake III engine GPL2
+// source release, file bg_slidemove.c). This doesn't work reliably for
+// reasons that are unclear to me; I would guess it has to do with its
+// complex use of an array. I've left it here commented out for reference
+// purposes. -- CEV
+//----------------------------------------------------------------------
+#if 0
+int(float dogravity) PM_Q3SlideMove =
+{
+ vector planes[PHYS_SLIDE_MAX_CLIP_PLANES];
+ vector dir, end, new_v, start_v;
+ vector grav_v, gnew_v;
+ int blocked, bumpcount, numplanes, i, j, k;
+ float d, time_left, ent_grav;
+
+ blocked = 0;
+
+ // initialize all these vectors
+ for (i = 0; i < PHYS_SLIDE_MAX_CLIP_PLANES; i++)
+ planes[i] = '0 0 0';
+ dir = end = new_v = grav_v = gnew_v = '0 0 0';
+ start_v = self.velocity;
+
+ if (dogravity)
+ {
+ grav_v = self.velocity;
+ if (self.gravity)
+ ent_grav = self.gravity;
+ else
+ ent_grav = 1.0;
+ grav_v_z -= ent_grav * cvar("sv_gravity") * input_timelength;
+ self.velocity_z = (self.velocity_z + grav_v_z) * 0.5;
+ start_v_z = grav_v_z;
+ if (self.groundnormal)
+ {
+ self.velocity = phys_clipvelocity (self.velocity,
+ self.groundnormal, PM_OVERCLIP, FALSE);
+ }
+ }
+
+ if (self.groundnormal)
+ {
+ numplanes = 1;
+ planes[0] = self.groundnormal;
+ }
+ else
+ numplanes = 0;
+
+ // never turn against original velocity
+ planes[numplanes] = normalize (self.velocity);
+ numplanes++;
+
+ for (bumpcount = 0, time_left = input_timelength;
+ time_left > 0 && bumpcount < PHYS_SLIDE_NUMBUMPS; bumpcount++)
+ {
+ end = self.origin + self.velocity * time_left;
+ tracebox(self.origin, self.mins, self.maxs, end, FALSE, self);
+
+ if (trace_allsolid || trace_startsolid)
+ {
+ // entity is trapped in a solid; attempt to nudge out
+ if (phys_nudge()) continue;
+
+ // nah, we're stuck. don't build up falling damage
+ // but allow sideways acceleration
+ dprint ("PM_Q3SlideMove: entity trapped in a solid\n");
+ self.velocity_z = 0;
+ return 3;
+ }
+
+ float cur_fraction = trace_fraction;
+ vector cur_plane = trace_plane_normal;
+
+ if (cur_fraction > 0)
+ self.origin = trace_endpos;
+
+ if (cur_fraction == 1) break;
+
+ /*
+ // this isn't working for some reason -- CEV
+ if (cur_plane.z)
+ if (cur_plane.z > 0.7)
+ {
+ blocked |= 1;
+ self.flags |= FL_ONGROUND;
+ self.groundnormal = cur_plane;
+ self.groundentity = trace_ent;
+ }
+ else
+ blocked |= 2;
+ */
+
+ phys_dotouch (trace_ent);
+ time_left -= time_left * cur_fraction;
+
+ if (numplanes >= PHYS_SLIDE_MAX_CLIP_PLANES)
+ {
+ // this shouldn't really happen
+ if (cvar("developer"))
+ {
+ dprint ("PM_Q3SlideMove: numplanes >= max: ");
+ dprint (ftos(numplanes));
+ dprint ("\n");
+ }
+ self.velocity = '0 0 0';
+ return 7;
+ }
+
+ // comment from Q3 source:
+ // if this is the same plane we hit before nudge velocity out
+ // along it; this fixes some issues with non-axial planes
+ for (i = 0; i < numplanes; i++)
+ {
+ if (cur_plane * planes[i] > 0.99)
+ {
+ dprint ("PM_Q3SlideMove: non-axial plane\n");
+ self.velocity += cur_plane;
+ break;
+ }
+ }
+
+ if (i < numplanes) continue;
+
+ planes[numplanes] = cur_plane;
+ numplanes++;
+
+ // modify velocity so it parallels all of the clip planes
+ // find a plane that it enters
+ for (i = 0; i < numplanes; i++)
+ {
+ if (self.velocity * planes[i] >= 0.1)
+ continue;
+
+ // slide along the plane
+ new_v = phys_clipvelocity (self.velocity, planes[i],
+ PM_OVERCLIP, FALSE);
+ if (dogravity)
+ gnew_v = phys_clipvelocity (grav_v,
+ planes[i], PM_OVERCLIP, FALSE);
+
+ for (j = 0; j < numplanes; j++)
+ {
+ if (new_v * planes[j] >= 0.1) continue;
+
+ // try clipping the move to the plane
+ new_v = phys_clipvelocity (new_v, planes[j],
+ PM_OVERCLIP, FALSE);
+ if (dogravity)
+ gnew_v = phys_clipvelocity (gnew_v,
+ planes[j], PM_OVERCLIP, FALSE);
+
+ // see if it goes back into the first clip plane
+ if (new_v * planes[i] >= 0) continue;
+
+ // slide along the crease
+ dir = crossproduct (planes[i], planes[j]);
+ dir = normalize (dir);
+ d = dir * self.velocity;
+ new_v = dir * d;
+
+ if (dogravity)
+ {
+ dir = crossproduct(planes[i],planes[j]);
+ dir = normalize (dir);
+ d = dir * grav_v;
+ gnew_v = dir * d;
+ }
+
+ // see if the move enters a third plane
+ for (k = 0; k < numplanes; k++)
+ {
+ if (k == i || k == j)
+ continue;
+
+ if (new_v * planes[k] >= 0.1)
+ // no interaction
+ continue;
+
+ // testing something here -- CEV
+ if (numplanes < 3)
+ continue;
+
+ // stop at a triple plane interaction
+ if (cvar("developer"))
+ {
+ // laugh at my 80 col terminal
+ dprint ("PM_Q3SlideMove: ");
+ dprint ("triple plane inter");
+ dprint ("action; numplanes: ");
+ dprint (ftos(numplanes));
+ dprint ("\n");
+ }
+ self.velocity = '0 0 0';
+ return 7;
+ }
+ }
+
+ // if we've fixed all interaction try another move
+ self.velocity = new_v;
+ if (dogravity) grav_v = gnew_v;
+ break;
+ }
+ }
+
+ // final gravity check here
+ if (dogravity) self.velocity = grav_v;
+
+ // wallclip / wall skim timer check
+ if ((self.primal_speed > PM_MAXSPEED) &&
+ (self.jump_time) &&
+ (!(self.flags & FL_WATERJUMP)) &&
+ (self.teleport_time <= (time - 0.1)) &&
+ (self.boost_time <= time - PM_BOOST_WINDOW) &&
+ (self.jump_time > (time - PM_WALLCLIP_WINDOW)) &&
+ (vlen(start_v) > vlen(self.velocity)))
+ {
+ // dprint ("PM_Q3SlideMove: restoring velocity...\n");
+ self.velocity = start_v;
+ }
+
+ if (bumpcount > 0) blocked |= 8;
+ return blocked;
+};
+#endif
+
+//======================================================================
// PM_Q1FlyMove
//
// Based on Quake 1's SV_FlyMove function (found in id's engine, QuakeSpasm,
@@ -1027,6 +1296,7 @@ vector(vector vel, vector normal, float ob, float oneside) PM_ClipVelocity =
//----------------------------------------------------------------------
#if 0
#define PM_SLIDE_MAX_CLIP_PLANES 5
+#define PM_SLIDE_NUMBUMPS 5
int(float dogravity, float onesided) PM_Q1FlyMove =
{
vector planes[PM_SLIDE_MAX_CLIP_PLANES];
@@ -1349,4 +1619,3 @@ void(float dogravity, float onesided) PM_StepSlideMove =
}
};
#endif
-

Return to the top of this page or return to the overview of this repo.