Skip to content

Commit

Permalink
Hexen uncapped framerate (#854)
Browse files Browse the repository at this point in the history
* Bind crispy_uncapped in Hexen

* Minimal uncapped framerate implementation

* Implement uncapped quake

* Implement uncapped sector height change

* Interpolate moving polyobjects

* Improve interpolation of moving polyobjects

Fixes out-of-sync polyobjects on Griffin Chapel. Thanks to @JNechaevsky
for letting me know about that particular stress test.

* Polyobject movment interpolation take 3

Handle interpolation in a more straightforward way.
Fix synchronization issues.
Add comments.

This could probably be handled better, but perfect is the enemy of done!

* Interpolate rotating polyobjects

* Properly adjust seg angles for rotating polys

No surprise, this greatly improves the smoothness of rotating poly
objects.

* Improve smoothness of poly movement interpolation

Fix logic error which was causing interpolation to terminate early,
resulting in stutter.

Improve handling of the first frame after starting a new gametic.

Fix dx and dy not being properly initialized. This would sometimes cause
polyobjects to shake when stopped.

* Poly movement refactor

Make the poly movement code mirror the poly rotation code. We now move
vertices, calculate the bounding boxes and then undo the movement if
interpolating.

Exit PO_InterpolatePolyObjects immediately if not interpolating.

* Remove some obsolete conditions

* Implement smooth wall texture scrolling

* Implement smooth flat scrolling

Thanks to @JNechaevsky for figuring out the trick with using the
ds_*frac values!

* Fix flat scrolling hitch when pausing

* Add uncapped framerate option to Crispness menu
  • Loading branch information
mikeday0 authored Apr 13, 2022
1 parent ae222fb commit 0ab96c0
Show file tree
Hide file tree
Showing 17 changed files with 588 additions and 96 deletions.
3 changes: 3 additions & 0 deletions src/hexen/g_game.c
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,9 @@ void G_Ticker(void)
inventory = false;
cmd->arti = 0;
}

oldleveltime = leveltime; // [crispy] Track if game is running

//
// do main actions
//
Expand Down
1 change: 1 addition & 0 deletions src/hexen/h2_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ void D_BindVariables(void)
M_BindIntVariable("crispy_smoothscaling", &crispy->smoothscaling);
M_BindIntVariable("crispy_vsync", &crispy->vsync);
M_BindIntVariable("crispy_widescreen", &crispy->widescreen);
M_BindIntVariable("crispy_uncapped", &crispy->uncapped);
}

// Set the default directory where hub savegames are saved.
Expand Down
14 changes: 14 additions & 0 deletions src/hexen/h2def.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@ typedef struct mobj_s
short tid; // thing identifier
byte special; // special
byte args[5]; // special arguments

// [AM] If true, ok to interpolate this tic.
int interp;

// [AM] Previous position of mobj before think.
// Used to interpolate between positions.
fixed_t oldx;
fixed_t oldy;
fixed_t oldz;
angle_t oldangle;
} mobj_t;

// each sector has a degenmobj_t in it's center for sound origin purposes
Expand Down Expand Up @@ -567,6 +577,10 @@ typedef struct player_s
int morphTics; // player is a pig if > 0
unsigned int jumpTics; // delay the next jump for a moment
unsigned int worldTimer; // total time the player's been playing

// [AM] Previous position of viewz before think.
// Used to interpolate between camera positions.
angle_t oldviewz;
} player_t;

#define CF_NOCLIP 1
Expand Down
14 changes: 12 additions & 2 deletions src/hexen/mn_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ static boolean SCNetCheck(int option);
static void CrispyHires(int option);
static void CrispyToggleWidescreen(int option);
static void CrispySmoothing(int option);
static void CrispyUncapped(int option);
static void CrispyVsync(int option);
static void SCNetCheck2(int option);
static void SCLoadGame(int option);
Expand Down Expand Up @@ -301,13 +302,14 @@ static MenuItem_t CrispnessItems[] = {
{ITT_LRFUNC, "HIGH RESOLUTION RENDERING:", CrispyHires, 0, MENU_NONE},
{ITT_LRFUNC, "ASPECT RATIO:", CrispyToggleWidescreen, 0, MENU_NONE},
{ITT_LRFUNC, "SMOOTH PIXEL SCALING:", CrispySmoothing, 0, MENU_NONE},
{ITT_LRFUNC, "UNCAPPED FRAMERATE:", CrispyUncapped, 0, MENU_NONE},
{ITT_LRFUNC, "ENABLE VSYNC:", CrispyVsync, 0, MENU_NONE}
};

static Menu_t CrispnessMenu = {
68, 40,
DrawCrispnessMenu,
4, CrispnessItems,
5, CrispnessItems,
0,
MENU_OPTIONS
};
Expand Down Expand Up @@ -1253,6 +1255,11 @@ static void CrispySmoothing(int option)
crispy->smoothscaling = !crispy->smoothscaling;
}

static void CrispyUncapped(int option)
{
crispy->uncapped = !crispy->uncapped;
}

static void CrispyVsyncHook(void)
{
crispy->vsync = !crispy->vsync;
Expand Down Expand Up @@ -2034,8 +2041,11 @@ static void DrawCrispnessMenu(void)
// Smooth pixel scaling
MN_DrTextA(crispy->smoothscaling ? "ON" : "OFF", 216, 60);

// Uncapped framerate
MN_DrTextA(crispy->uncapped ? "ON" : "OFF", 217, 70);

// Vsync
MN_DrTextA(crispy->vsync ? "ON" : "OFF", 167, 70);
MN_DrTextA(crispy->vsync ? "ON" : "OFF", 167, 80);

dp_translation = NULL;
}
62 changes: 58 additions & 4 deletions src/hexen/p_anim.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#define LIGHTNING_SPECIAL2 199
#define SKYCHANGE_SPECIAL 200

#define TEXTURE_SCROLL_SHIFT 10 // [crispy]

// TYPES -------------------------------------------------------------------

typedef struct
Expand Down Expand Up @@ -142,16 +144,28 @@ void P_AnimateSurfaces(void)
switch (line->special)
{
case 100: // Scroll_Texture_Left
sides[line->sidenum[0]].textureoffset += line->arg1 << 10;
sides[line->sidenum[0]].basetextureoffset +=
line->arg1 << TEXTURE_SCROLL_SHIFT;
sides[line->sidenum[0]].textureoffset =
sides[line->sidenum[0]].basetextureoffset;
break;
case 101: // Scroll_Texture_Right
sides[line->sidenum[0]].textureoffset -= line->arg1 << 10;
sides[line->sidenum[0]].basetextureoffset -=
line->arg1 << TEXTURE_SCROLL_SHIFT;
sides[line->sidenum[0]].textureoffset =
sides[line->sidenum[0]].basetextureoffset;
break;
case 102: // Scroll_Texture_Up
sides[line->sidenum[0]].rowoffset += line->arg1 << 10;
sides[line->sidenum[0]].baserowoffset +=
line->arg1 << TEXTURE_SCROLL_SHIFT;
sides[line->sidenum[0]].rowoffset =
sides[line->sidenum[0]].baserowoffset;
break;
case 103: // Scroll_Texture_Down
sides[line->sidenum[0]].rowoffset -= line->arg1 << 10;
sides[line->sidenum[0]].baserowoffset -=
line->arg1 << TEXTURE_SCROLL_SHIFT;
sides[line->sidenum[0]].rowoffset =
sides[line->sidenum[0]].baserowoffset;
break;
}
}
Expand All @@ -173,6 +187,46 @@ void P_AnimateSurfaces(void)
}
}

extern fixed_t fractionaltic; // [crispy]

// [crispy] smooth texture scrolling
void R_InterpolateTextureOffset(void)
{
const line_t* line;
side_t* side;
int i;

if (!(crispy->uncapped && leveltime > oldleveltime))
{
return;
}

for (i = 0; i < numlinespecials; i++)
{
line = linespeciallist[i];
side = &sides[line->sidenum[0]];

switch (line->special)
{
case 100: // Scroll_Texture_Right
side->textureoffset = side->basetextureoffset +
(FixedMul(fractionaltic, line->arg1) << TEXTURE_SCROLL_SHIFT);
break;
case 101: // Scroll_Texture_Left
side->textureoffset = side->basetextureoffset -
(FixedMul(fractionaltic, line->arg1) << TEXTURE_SCROLL_SHIFT);
break;
case 102: // Scroll_Texture_Up
side->rowoffset = side->baserowoffset +
(FixedMul(fractionaltic, line->arg1) << TEXTURE_SCROLL_SHIFT);
break;
case 103: // Scroll_Texture_Down
side->rowoffset = side->baserowoffset -
(FixedMul(fractionaltic, line->arg1) << TEXTURE_SCROLL_SHIFT);
break;
}
}
}
//==========================================================================
//
// P_LightningFlash
Expand Down
5 changes: 5 additions & 0 deletions src/hexen/p_floor.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ result_e T_MovePlane(sector_t * sector, fixed_t speed,
boolean flag;
fixed_t lastpos;

// [AM] Store old sector heights for interpolation.
sector->oldfloorheight = sector->floorheight;
sector->oldceilingheight = sector->ceilingheight;
sector->oldgametic = gametic;

switch (floorOrCeiling)
{
case 0: // FLOOR
Expand Down
34 changes: 34 additions & 0 deletions src/hexen/p_mobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,27 @@ static void PlayerLandedOnThing(mobj_t * mo, mobj_t * onmobj)
void P_MobjThinker(mobj_t * mobj)
{
mobj_t *onmo;

// [crispy] suppress interpolation of player missiles for the first tic
if (mobj->interp == -1)
{
mobj->interp = false;
}
else
// [AM] Handle interpolation unless we're an active player.
if (!(mobj->player != NULL && mobj == mobj->player->mo))
{
// Assume we can interpolate at the beginning
// of the tic.
mobj->interp = true;

// Store starting position for mobj interpolation.
mobj->oldx = mobj->x;
mobj->oldy = mobj->y;
mobj->oldz = mobj->z;
mobj->oldangle = mobj->angle;
}

/*
// Reset to not blasted when momentums are gone
if((mobj->flags2&MF2_BLASTED) && (!(mobj->momx)) && (!(mobj->momy)))
Expand Down Expand Up @@ -1246,6 +1267,15 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
mobj->floorclip = 0;
}

// [AM] Do not interpolate on spawn.
mobj->interp = false;

// [AM] Just in case interpolation is attempted...
mobj->oldx = mobj->x;
mobj->oldy = mobj->y;
mobj->oldz = mobj->z;
mobj->oldangle = mobj->angle;

mobj->thinker.function = P_MobjThinker;
P_AddThinker(&mobj->thinker);
return (mobj);
Expand Down Expand Up @@ -2283,6 +2313,10 @@ mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type)
MissileMobj->y += (MissileMobj->momy >> 1);
MissileMobj->z += (MissileMobj->momz >> 1);
}

// [crispy] suppress interpolation of player missiles for the first tic
MissileMobj->interp = -1;

if (!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y))
{ // Exploded immediately
P_ExplodeMissile(MissileMobj);
Expand Down
11 changes: 11 additions & 0 deletions src/hexen/p_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,16 @@ void P_LoadSectors(int lump)
ss->tag = SHORT(ms->tag);
ss->thinglist = NULL;
ss->seqType = SEQTYPE_STONE; // default seqType

// [AM] Sector interpolation. Even if we're
// not running uncapped, the renderer still
// uses this data.
ss->oldfloorheight = ss->floorheight;
ss->interpfloorheight = ss->floorheight;
ss->oldceilingheight = ss->ceilingheight;
ss->interpceilingheight = ss->ceilingheight;
// [crispy] inhibit sector interpolation during the 0th gametic
ss->oldgametic = -1;
}
W_ReleaseLumpNum(lump);
}
Expand Down Expand Up @@ -691,6 +701,7 @@ void P_SetupLevel(int episode, int map, int playermask, skill_t skill)

P_InitThinkers();
leveltime = 0;
oldleveltime = 0; // [crispy] Track if game is running

M_snprintf(lumpname, sizeof(lumpname), "MAP%02d", map);
lumpnum = W_GetNumForName(lumpname);
Expand Down
11 changes: 11 additions & 0 deletions src/hexen/p_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,17 @@ void P_PlayerThink(player_t * player)
int floorType;
mobj_t *pmo;

// [AM] Assume we can interpolate at the beginning
// of the tic.
player->mo->interp = true;

// [AM] Store starting position for player interpolation.
player->mo->oldx = player->mo->x;
player->mo->oldy = player->mo->y;
player->mo->oldz = player->mo->z;
player->mo->oldangle = player->mo->angle;
player->oldviewz = player->viewz;

// No-clip cheat
if (player->cheats & CF_NOCLIP)
{
Expand Down
Loading

0 comments on commit 0ab96c0

Please sign in to comment.