Merge branch 'better-local-camera' into 'master'

Better local camera handling

See merge request KartKrew/Kart!999
This commit is contained in:
Oni 2023-03-03 07:08:19 +00:00
commit 9eefb2e0ae
3 changed files with 158 additions and 20 deletions

View file

@ -2112,6 +2112,78 @@ static void P_3dMovement(player_t *player)
}
}
// For turning correction in P_UpdatePlayerAngle.
// Given a range of possible steering inputs, finds a steering input that corresponds to the desired angle change.
static INT16 P_FindClosestTurningForAngle(player_t *player, INT32 targetAngle, INT16 lowBound, INT16 highBound)
{
INT16 newBound;
INT16 preferred = lowBound;
int attempts = 0;
// Only works if our low bound is actually our low bound.
if (highBound < lowBound)
{
INT16 tmp = lowBound;
lowBound = highBound;
highBound = tmp;
}
// Slightly frumpy binary search for the ideal turning input.
// We do this instead of reversing K_GetKartTurnValue so that future handling changes are automatically accounted for.
while (attempts++ < 20) // Practical calls of this function search maximum 10 times, this is solely for safety.
{
// These need to be treated as signed, or situations where boundaries straddle 0 are a mess.
INT32 lowAngle = K_GetKartTurnValue(player, lowBound) << TICCMD_REDUCE;
INT32 highAngle = K_GetKartTurnValue(player, highBound) << TICCMD_REDUCE;
// EXIT CONDITION 1: Hopeless search, target angle isn't between boundaries at all.
if (lowAngle >= targetAngle)
return lowBound;
if (highAngle <= targetAngle)
return highBound;
// Test the middle of our steering range, so we can see which side is more promising.
newBound = (lowBound + highBound) / 2;
// EXIT CONDITION 2: Boundaries converged and we're all out of precision.
if (newBound == lowBound || newBound == highBound)
break;
INT32 newAngle = K_GetKartTurnValue(player, newBound) << TICCMD_REDUCE;
angle_t lowError = abs(targetAngle - lowAngle);
angle_t highError = abs(targetAngle - highAngle);
angle_t newError = abs(targetAngle - newAngle);
// CONS_Printf("steering %d / %d / %d - angle %d / %d / %d - TA %d - error %d / %d / %d\n", lowBound, newBound, highBound, lowAngle, newAngle, highAngle, targetAngle, lowError, newError, highError);
// EXIT CONDITION 3: We got lucky!
if (lowError == 0)
return lowBound;
if (newError == 0)
return newBound;
if (highError == 0)
return highBound;
// If not, store the best estimate...
if (lowError <= newError && lowError <= highError)
preferred = lowBound;
if (highError <= newError && highError <= lowError)
preferred = highBound;
if (newError <= lowError && newError <= highError)
preferred = newBound;
// ....and adjust the bounds for another run.
if (lowAngle <= targetAngle && targetAngle <= newAngle)
highBound = newBound;
else
lowBound = newBound;
}
return preferred;
}
//
// P_UpdatePlayerAngle
//
@ -2148,6 +2220,13 @@ static void P_UpdatePlayerAngle(player_t *player)
angle_t targetAngle = (player->cmd.angle) << TICCMD_REDUCE;
angle_t targetDelta = targetAngle - (player->mo->angle);
// Corrections via fake turn go through easing.
// That means undoing them takes the same amount of time as doing them.
// This can lead to oscillating death spiral states on a multi-tic correction, as we swing past the target angle.
// So before we go into death-spirals, if our predicton is _almost_ right...
angle_t leniency = (2*ANG1/3) * min(player->cmd.latency, 6);
// Don't force another turning tic, just give them the desired angle!
if (targetDelta == angleChange || player->pflags & PF_DRIFTEND || (maxTurnRight == 0 && maxTurnLeft == 0))
{
// We are where we need to be.
@ -2158,25 +2237,15 @@ static void P_UpdatePlayerAngle(player_t *player)
// so we momentarily ignore the camera angle and let the server trust our inputs instead.
// That way, even if you're steering blind, you get the intended "kick-out" effect.
}
else if (targetDelta >= ANGLE_180 && maxTurnLeft >= targetDelta) // Overshot, so just fudge it.
else
{
angleChange = targetDelta;
player->steering = targetsteering;
}
else if (targetDelta <= ANGLE_180 && maxTurnRight <= targetDelta) // Overshot, so just fudge it.
{
angleChange = targetDelta;
player->steering = targetsteering;
}
else if (targetDelta >= ANGLE_180 && maxTurnLeft < targetDelta) // Undershot, slam the stick.
{
angleChange = maxTurnLeft;
player->steering = steeringLeft;
}
else if (targetDelta <= ANGLE_180 && maxTurnRight > targetDelta) // Undershot, slam the stick.
{
angleChange = maxTurnRight;
player->steering = steeringRight;
// We're off. Try to legally steer the player towards their camera.
player->steering = P_FindClosestTurningForAngle(player, targetDelta, steeringLeft, steeringRight);
angleChange = K_GetKartTurnValue(player, player->steering) << TICCMD_REDUCE;
// And if the resulting steering input is close enough, snap them exactly.
if (min(targetDelta - angleChange, angleChange - targetDelta) <= leniency)
angleChange = targetDelta;
}
}
else
@ -2213,6 +2282,7 @@ static void P_UpdatePlayerAngle(player_t *player)
}
}
//
// P_SpectatorMovement
//

View file

@ -1065,6 +1065,25 @@ static void R_DrawVisSprite(vissprite_t *vis)
pwidth = patch->width;
#endif
if (vis->x1test && vis->x2test)
{
INT32 x1test = vis->x1test;
INT32 x2test = vis->x2test;
if (x1test < 0)
x1test = 0;
if (x2test >= vid.width)
x2test = vid.width-1;
const INT32 t = (vis->startfrac + (vis->xiscale * (x2test - x1test))) >> FRACBITS;
if (x1test <= x2test && (t < 0 || t >= pwidth))
{
CONS_Printf("THE GAME WOULD HAVE CRASHED, %d (old) vs %d (new)\n", (x2test - x1test), (vis->x2 - vis->x1));
}
}
// Non-paper drawing loop
for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
{
@ -1452,6 +1471,9 @@ static void R_ProjectDropShadow(
shadow->x1 = x1 < portalclipstart ? portalclipstart : x1;
shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
shadow->x1test = 0;
shadow->x2test = 0;
shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000
shadow->scale = FixedMul(yscale, shadowyscale);
shadow->thingscale = interp.scale;
@ -1598,6 +1620,9 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
box->sortscale = yscale;
box->dispoffset = 0;
}
box->x1test = 0;
box->x2test = 0;
}
//
@ -1616,6 +1641,7 @@ static void R_ProjectSprite(mobj_t *thing)
fixed_t sort_x = 0, sort_y = 0, sort_z;
INT32 x1, x2;
INT32 x1test = 0, x2test = 0;
spritedef_t *sprdef;
spriteframe_t *sprframe;
@ -2001,18 +2027,35 @@ static void R_ProjectSprite(mobj_t *thing)
scalestep = 0;
yscale = sortscale;
tx += offset;
x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
//x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
x1 = centerx + (FixedMul(tx,xscale) / FRACUNIT);
x1test = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
if (x1test > viewwidth)
x1test = 0;
// off the right side?
if (x1 > viewwidth)
return;
tx += offset2;
x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
//x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
x2 = (centerx + (FixedMul(tx,xscale) / FRACUNIT)) - 1;
x2test = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS) - 1;
if (x2test < 0)
x2test = 0;
// off the left side
if (x2 < 0)
return;
#if 0
if ((x2 - x1) != (x2test - x1test))
CONS_Printf("[%d] %d != %d\n", objectsdrawn, x2 - x1, x2test - x1test);
#endif
}
// Adjust the sort scale if needed
@ -2101,6 +2144,12 @@ static void R_ProjectSprite(mobj_t *thing)
if (x2 < portalclipstart || x1 >= portalclipend)
return;
if (x2test < portalclipstart || x1test >= portalclipend)
{
x1test = 0;
x2test = 0;
}
if (P_PointOnLineSide(interp.x, interp.y, portalclipline) != 0)
return;
}
@ -2269,6 +2318,9 @@ static void R_ProjectSprite(mobj_t *thing)
vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
vis->x1test = x1test < portalclipstart ? portalclipstart : x1test;
vis->x2test = x2test >= portalclipend ? portalclipend-1 : x2test;
vis->sector = thing->subsector->sector;
vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
@ -2290,6 +2342,12 @@ static void R_ProjectSprite(mobj_t *thing)
if (shadowdraw || shadoweffects)
{
if (x1test && x2test)
{
vis->xiscaletest = (patch->width<<FRACBITS)/(x2test-x1test+1); // fuck it
x1test += (x2test-x1test)/2; // reusing x1 variable
}
iscale = (patch->width<<FRACBITS)/(x2-x1+1); // fuck it
x1 += (x2-x1)/2; // reusing x1 variable
vis->shear.offset = vis->x1-x1;
@ -2303,6 +2361,7 @@ static void R_ProjectSprite(mobj_t *thing)
{
vis->startfrac = spr_width-1;
vis->xiscale = -iscale;
vis->xiscaletest = -vis->xiscaletest;
}
else
{
@ -2310,10 +2369,13 @@ static void R_ProjectSprite(mobj_t *thing)
vis->xiscale = iscale;
}
vis->startfractest = vis->startfrac;
if (vis->x1 > x1)
{
vis->startfrac += FixedDiv(vis->xiscale, this_scale) * (vis->x1 - x1);
vis->scale += FixedMul(scalestep, spriteyscale) * (vis->x1 - x1);
vis->startfractest += FixedDiv(vis->xiscaletest, this_scale) * (vis->x1test - x1test);
}
vis->transmap = R_GetBlendTable(blendmode, trans);
@ -2525,6 +2587,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
vis->x1test = 0;
vis->x2test = 0;
vis->xscale = xscale; //SoM: 4/17/2000
vis->sector = thing->subsector->sector;
vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS);

View file

@ -161,6 +161,9 @@ struct vissprite_t
mobj_t *mobj; // for easy access
INT32 x1, x2;
INT32 x1test, x2test;
fixed_t xiscaletest;
fixed_t startfractest;
fixed_t gx, gy; // for line side calculation
fixed_t gz, gzt; // global bottom/top for silhouette clipping and sorting with 3D floors