From b625076c8789d0692f5ae721396914766e577255 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 6 Oct 2019 17:40:52 -0500 Subject: [PATCH] Implement interpolation at the renderer level Instead of interpolating thinkers, we interpolate mobjs inside the renderer. Further interpolation is TBI. --- src/Makefile.d/versions.mk | 2 +- src/Sourcefile | 1 + src/android/i_system.c | 6 ++ src/d_clisrv.c | 1 + src/d_main.c | 21 ++++- src/d_main.h | 1 + src/dummy/i_system.c | 2 +- src/g_game.c | 10 +- src/hardware/hw_main.c | 72 ++++++++++++--- src/i_system.h | 4 + src/p_mobj.c | 25 +++++ src/p_mobj.h | 2 + src/p_tick.c | 1 + src/r_fps.c | 169 ++++++++++++++++++++++++++++++++++ src/r_fps.h | 59 ++++++++++++ src/r_main.c | 182 +++++++++++++++++-------------------- src/r_main.h | 6 ++ src/r_things.c | 83 +++++++++++++---- src/sdl/i_system.c | 27 ++++-- src/win32/win_sys.c | 6 ++ 20 files changed, 537 insertions(+), 143 deletions(-) create mode 100644 src/r_fps.c create mode 100644 src/r_fps.h diff --git a/src/Makefile.d/versions.mk b/src/Makefile.d/versions.mk index f0b59658e..d2877b374 100644 --- a/src/Makefile.d/versions.mk +++ b/src/Makefile.d/versions.mk @@ -156,7 +156,7 @@ ifdef DEBUGMODE ifdef GCC48 opts+=-Og else -opts+=O0 +opts+=-O0 endif endif diff --git a/src/Sourcefile b/src/Sourcefile index 983dadaf0..ef9fbdc13 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -55,6 +55,7 @@ tables.c r_bsp.c r_data.c r_draw.c +r_fps.c r_main.c r_plane.c r_segs.c diff --git a/src/android/i_system.c b/src/android/i_system.c index 752e9f6c6..7256bac34 100644 --- a/src/android/i_system.c +++ b/src/android/i_system.c @@ -88,6 +88,12 @@ tic_t I_GetTime(void) return (since_start*TICRATE)/1000000; } +fixed_t I_GetTimeFrac(void) +{ + //stub + return 0; +} + void I_Sleep(void){} void I_GetEvent(void){} diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ac8bba608..70e283bba 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5270,6 +5270,7 @@ void TryRunTics(tic_t realtics) boolean update_stats = !(paused || P_AutoPause()); DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + prev_tics = I_GetTime(); if (update_stats) PS_START_TIMING(ps_tictime); diff --git a/src/d_main.c b/src/d_main.c index fa9e21337..0281ef86c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -64,6 +64,7 @@ #include "deh_tables.h" // Dehacked list test #include "m_cond.h" // condition initialization #include "fastcmp.h" +#include "r_fps.h" // Frame interpolation/uncapped #include "keys.h" #include "filesrch.h" // refreshdirmenu #include "g_input.h" // tutorial mode control scheming @@ -147,6 +148,7 @@ event_t events[MAXEVENTS]; INT32 eventhead, eventtail; boolean dedicated = false; +boolean tic_happened = false; // Frame interpolation/uncapped // // D_PostEvent @@ -764,7 +766,7 @@ void D_SRB2Loop(void) debugload--; #endif - if (!realtics && !singletics) + if (!realtics && !singletics && cv_frameinterpolation.value != 1) { I_Sleep(); continue; @@ -780,15 +782,27 @@ void D_SRB2Loop(void) realtics = 1; // process tics (but maybe not if realtic == 0) + tic_happened = realtics ? true : false; TryRunTics(realtics); + if (cv_frameinterpolation.value == 1) + rendertimefrac = I_GetTimeFrac(); + else + rendertimefrac = FRACUNIT; + + if (cv_frameinterpolation.value == 1) + { + D_Display(); + } + if (lastdraw || singletics || gametic > rendergametic) { rendergametic = gametic; rendertimeout = entertic+TICRATE/17; // Update display, next frame, with current state. - D_Display(); + // (Only display if not already done for frame interp) + cv_frameinterpolation.value == 0 ? D_Display() : 0; if (moviemode) M_SaveFrame(); @@ -805,7 +819,8 @@ void D_SRB2Loop(void) if (camera.chase) P_MoveChaseCamera(&players[displayplayer], &camera, false); } - D_Display(); + // (Only display if not already done for frame interp) + cv_frameinterpolation.value == 0 ? D_Display() : 0; if (moviemode) M_SaveFrame(); diff --git a/src/d_main.h b/src/d_main.h index 8189a9f2b..a06334b6e 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -26,6 +26,7 @@ extern char srb2home[256]; //Alam: My Home extern boolean usehome; //Alam: which path? extern const char *pandf; //Alam: how to path? extern char srb2path[256]; //Alam: SRB2's Home +extern boolean tic_happened; // Frame interpolation/uncapped // the infinite loop of D_SRB2Loop() called from win_main for windows version void D_SRB2Loop(void) FUNCNORETURN; diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c index 4a657ed19..f3d0bc5e8 100644 --- a/src/dummy/i_system.c +++ b/src/dummy/i_system.c @@ -16,7 +16,7 @@ tic_t I_GetTime(void) return 0; } -int I_GetTimeMicros(void) +fixed_t I_GetTimeFrac(void) { return 0; } diff --git a/src/g_game.c b/src/g_game.c index 39d003056..fb1c0d522 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -46,6 +46,7 @@ #include "b_bot.h" #include "m_cond.h" // condition sets #include "lua_script.h" +#include "r_fps.h" // frame interpolation/uncapped #include "lua_hud.h" @@ -2362,6 +2363,8 @@ void G_Ticker(boolean run) F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); + R_UpdateViewInterpolation(); + break; case GS_INTERMISSION: @@ -2414,7 +2417,12 @@ void G_Ticker(boolean run) break; case GS_TITLESCREEN: - if (titlemapinaction) P_Ticker(run); // then intentionally fall through + if (titlemapinaction) + { + P_Ticker(run); + R_UpdateViewInterpolation(); + // then intentionally fall through + } /* FALLTHRU */ case GS_WAITINGPLAYERS: F_MenuPresTicker(run); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index fd8a23013..968ebc54e 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -38,6 +38,7 @@ #include "../m_cheat.h" #include "../f_finale.h" #include "../r_things.h" // R_GetShadowZ +#include "../d_main.h" #include "../p_slopes.h" #include "hw_md2.h" @@ -5050,6 +5051,12 @@ static void HWR_ProjectSprite(mobj_t *thing) INT32 rollangle = 0; #endif + // uncapped/interpolation + fixed_t interpx; + fixed_t interpy; + fixed_t interpz; + angle_t interpangle; + if (!thing) return; @@ -5071,13 +5078,26 @@ static void HWR_ProjectSprite(mobj_t *thing) dispoffset = thing->info->dispoffset; + interpx = thing->x; + interpy = thing->y; + interpz = thing->z; + interpangle = mobjangle; + + if (cv_frameinterpolation.value == 1 && !paused) + { + interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); + interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); + interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); + interpangle = mobjangle; + } + this_scale = FIXED_TO_FLOAT(thing->scale); spritexscale = FIXED_TO_FLOAT(thing->spritexscale); spriteyscale = FIXED_TO_FLOAT(thing->spriteyscale); // transform the origin point - tr_x = FIXED_TO_FLOAT(thing->x) - gl_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - gl_viewy; + tr_x = FIXED_TO_FLOAT(interpx) - gl_viewx; + tr_y = FIXED_TO_FLOAT(interpy) - gl_viewy; // rotation around vertical axis tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin); @@ -5100,8 +5120,8 @@ static void HWR_ProjectSprite(mobj_t *thing) } // The above can stay as it works for cutting sprites that are too close - tr_x = FIXED_TO_FLOAT(thing->x); - tr_y = FIXED_TO_FLOAT(thing->y); + tr_x = FIXED_TO_FLOAT(interpx); + tr_y = FIXED_TO_FLOAT(interpy); // decide which patch to use for sprite relative to player #ifdef RANGECHECK @@ -5149,7 +5169,7 @@ static void HWR_ProjectSprite(mobj_t *thing) I_Error("sprframes NULL for sprite %d\n", thing->sprite); #endif - ang = R_PointToAngle (thing->x, thing->y) - mobjangle; + ang = R_PointToAngle (interpx, interpy) - interpangle; if (mirrored) ang = InvAngle(ang); @@ -5295,12 +5315,12 @@ static void HWR_ProjectSprite(mobj_t *thing) if (vflip) { - gz = FIXED_TO_FLOAT(thing->z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); + gz = FIXED_TO_FLOAT(interpz + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale); } else { - gzt = FIXED_TO_FLOAT(thing->z) + (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); + gzt = FIXED_TO_FLOAT(interpz) + (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); gz = gzt - (FIXED_TO_FLOAT(spr_height) * this_yscale); } @@ -5461,6 +5481,9 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) unsigned rot = 0; UINT8 flip; + if (!thing) + return; + // Visibility check by the blend mode. if (thing->frame & FF_TRANSMASK) { @@ -5468,9 +5491,22 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) return; } + // uncapped/interpolation + fixed_t interpx = thing->x; + fixed_t interpy = thing->y; + fixed_t interpz = thing->z; + + // do interpolation + if (cv_frameinterpolation.value == 1 && !paused) + { + interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); + interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); + interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); + } + // transform the origin point - tr_x = FIXED_TO_FLOAT(thing->x) - gl_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - gl_viewy; + tr_x = FIXED_TO_FLOAT(interpx) - gl_viewx; + tr_y = FIXED_TO_FLOAT(interpy) - gl_viewy; // rotation around vertical axis tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin); @@ -5479,8 +5515,8 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) if (tz < ZCLIP_PLANE) return; - tr_x = FIXED_TO_FLOAT(thing->x); - tr_y = FIXED_TO_FLOAT(thing->y); + tr_x = FIXED_TO_FLOAT(interpx); + tr_y = FIXED_TO_FLOAT(interpy); // decide which patch to use for sprite relative to player if ((unsigned)thing->sprite >= numsprites) @@ -5542,7 +5578,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) vis->colormap = NULL; // set top/bottom coords - vis->gzt = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset); + vis->gzt = FIXED_TO_FLOAT(interpz + spritecachedinfo[lumpoff].topoffset); vis->gz = vis->gzt - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height); vis->precip = true; @@ -6678,6 +6714,8 @@ void HWR_DoPostProcessor(player_t *player) // 10 by 10 grid. 2 coordinates (xy) float v[SCREENVERTS][SCREENVERTS][2]; static double disStart = 0; + static float last_fractime = 0; + UINT8 x, y; INT32 WAVELENGTH; INT32 AMPLITUDE; @@ -6709,6 +6747,16 @@ void HWR_DoPostProcessor(player_t *player) HWD.pfnPostImgRedraw(v); if (!(paused || P_AutoPause())) disStart += 1; + fixed_t fractime = I_GetTimeFrac(); + if (tic_happened) + { + disStart = disStart - last_fractime + 1 + FIXED_TO_FLOAT(fractime); + } + else + { + disStart = disStart - last_fractime + FIXED_TO_FLOAT(fractime); + } + last_fractime = fractime; // Capture the screen again for screen waving on the intermission if(gamestate != GS_INTERMISSION) diff --git a/src/i_system.h b/src/i_system.h index 27fcdeb3f..0cdc7db85 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -46,6 +46,10 @@ UINT32 I_GetFreeMem(UINT32 *total); */ tic_t I_GetTime(void); +/** \brief Get the current time as a fraction of a tic since the last tic. +*/ +fixed_t I_GetTimeFrac(void); + /** \brief Returns precise time value for performance measurement. */ precise_t I_GetPreciseTime(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index e88cd4b9f..b52b9734f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4027,6 +4027,11 @@ void P_NullPrecipThinker(precipmobj_t *mobj) void P_SnowThinker(precipmobj_t *mobj) { + // reset old state (for interpolation) + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + P_CycleStateAnimation((mobj_t *)mobj); // adjust height @@ -4036,6 +4041,11 @@ void P_SnowThinker(precipmobj_t *mobj) void P_RainThinker(precipmobj_t *mobj) { + // reset old state (for interpolation) + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + P_CycleStateAnimation((mobj_t *)mobj); if (mobj->state != &states[S_RAIN1]) @@ -10032,6 +10042,11 @@ void P_MobjThinker(mobj_t *mobj) I_Assert(mobj != NULL); I_Assert(!P_MobjWasRemoved(mobj)); + // Set old position (for interpolation) + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + if (mobj->flags & MF_NOTHINK) return; @@ -10897,6 +10912,11 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) if (CheckForReverseGravity && !(mobj->flags & MF_NOBLOCKMAP)) P_CheckGravity(mobj, false); + // set old state too (for interpolation) + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + return mobj; } @@ -10944,6 +10964,11 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype || mobj->subsector->sector->floorpic == skyflatnum) mobj->precipflags |= PCF_PIT; + // set initial old positions (for interpolation) + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + return mobj; } diff --git a/src/p_mobj.h b/src/p_mobj.h index b1b79fd82..998baa19e 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -281,6 +281,7 @@ typedef struct mobj_s // Info for drawing: position. fixed_t x, y, z; + fixed_t old_x, old_y, old_z; // position interpolation // More list: links in sector (if needed) struct mobj_s *snext; @@ -408,6 +409,7 @@ typedef struct precipmobj_s // Info for drawing: position. fixed_t x, y, z; + fixed_t old_x, old_y, old_z; // position interpolation // More list: links in sector (if needed) struct precipmobj_s *snext; diff --git a/src/p_tick.c b/src/p_tick.c index 28ace9288..ca70fb926 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -23,6 +23,7 @@ #include "lua_hook.h" #include "m_perfstats.h" #include "i_system.h" // I_GetPreciseTime +#include "r_fps.h" // Object place #include "m_cheat.h" diff --git a/src/r_fps.c b/src/r_fps.c new file mode 100644 index 000000000..519288bab --- /dev/null +++ b/src/r_fps.c @@ -0,0 +1,169 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2000 by Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko (prboom) +// Copyright (C) 1999-2019 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_fps.h +/// \brief Uncapped framerate stuff. + +#include "r_fps.h" + +#include "r_main.h" +#include "g_game.h" +#include "i_video.h" +#include "r_plane.h" +#include "p_spec.h" +#include "r_state.h" +#ifdef HWRENDER +#include "hardware/hw_main.h" // for cv_glshearing +#endif + +static viewvars_t p1view_old; +static viewvars_t p1view_new; +static viewvars_t p2view_old; +static viewvars_t p2view_new; +static viewvars_t sky1view_old; +static viewvars_t sky1view_new; +static viewvars_t sky2view_old; +static viewvars_t sky2view_new; + +static viewvars_t *oldview = &p1view_old; +viewvars_t *newview = &p1view_new; + + +enum viewcontext_e viewcontext = VIEWCONTEXT_PLAYER1; + +// recalc necessary stuff for mouseaiming +// slopes are already calculated for the full possible view (which is 4*viewheight). +// 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) +static void R_SetupFreelook(player_t *player, boolean skybox) +{ +#ifndef HWRENDER + (void)player; + (void)skybox; +#endif + + // clip it in the case we are looking a hardware 90 degrees full aiming + // (lmps, network and use F12...) + if (rendermode == render_soft +#ifdef HWRENDER + || (rendermode == render_opengl + && (cv_glshearing.value == 1 + || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) +#endif + ) + { + G_SoftwareClipAimingPitch((INT32 *)&aimingangle); + } + + centeryfrac = (viewheight/2)<x + R_LerpFixed(oldview->x, newview->x, frac); + viewy = oldview->y + R_LerpFixed(oldview->y, newview->y, frac); + viewz = oldview->z + R_LerpFixed(oldview->z, newview->z, frac); + + viewangle = oldview->angle + R_LerpAngle(oldview->angle, newview->angle, frac); + aimingangle = oldview->aim + R_LerpAngle(oldview->aim, newview->aim, frac); + + viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); + viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + + // this is gonna create some interesting visual errors for long distance teleports... + // might want to recalculate the view sector every frame instead... + if (frac >= FRACUNIT) + { + viewplayer = newview->player; + viewsector = newview->sector; + } + else + { + viewplayer = oldview->player; + viewsector = oldview->sector; + } + + // well, this ain't pretty + if (newview == &sky1view_new || newview == &sky2view_new) + { + skybox = 1; + } + + R_SetupFreelook(newview->player, skybox); +} + +void R_UpdateViewInterpolation(void) +{ + p1view_old = p1view_new; + p2view_old = p2view_new; + sky1view_old = sky1view_new; + sky2view_old = sky2view_new; +} + +void R_SetViewContext(enum viewcontext_e _viewcontext) +{ + I_Assert(_viewcontext == VIEWCONTEXT_PLAYER1 + || _viewcontext == VIEWCONTEXT_PLAYER2 + || _viewcontext == VIEWCONTEXT_SKY1 + || _viewcontext == VIEWCONTEXT_SKY2); + viewcontext = _viewcontext; + + switch (viewcontext) + { + case VIEWCONTEXT_PLAYER1: + oldview = &p1view_old; + newview = &p1view_new; + break; + case VIEWCONTEXT_PLAYER2: + oldview = &p2view_old; + newview = &p2view_new; + break; + case VIEWCONTEXT_SKY1: + oldview = &sky1view_old; + newview = &sky1view_new; + break; + case VIEWCONTEXT_SKY2: + oldview = &sky2view_old; + newview = &sky2view_new; + break; + default: + I_Error("viewcontext value is invalid: we should never get here without an assert!!"); + break; + } +} + +fixed_t R_LerpFixed(fixed_t from, fixed_t to, fixed_t frac) +{ + return FixedMul(frac, to - from); +} + +INT32 R_LerpInt32(INT32 from, INT32 to, fixed_t frac) +{ + return FixedInt(FixedMul(frac, (to*FRACUNIT) - (from*FRACUNIT))); +} + +angle_t R_LerpAngle(angle_t from, angle_t to, fixed_t frac) +{ + return FixedMul(frac, to - from); +} diff --git a/src/r_fps.h b/src/r_fps.h new file mode 100644 index 000000000..f9ee6f0de --- /dev/null +++ b/src/r_fps.h @@ -0,0 +1,59 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2000 by Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko (prboom) +// Copyright (C) 1999-2019 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_fps.h +/// \brief Uncapped framerate stuff. + +#ifndef __R_FPS_H__ +#define __R_FPS_H__ + +#include "m_fixed.h" +#include "p_local.h" +#include "r_state.h" + +enum viewcontext_e +{ + VIEWCONTEXT_PLAYER1 = 0, + VIEWCONTEXT_PLAYER2, + VIEWCONTEXT_SKY1, + VIEWCONTEXT_SKY2 +}; + +typedef struct { + fixed_t x; + fixed_t y; + fixed_t z; + boolean sky; + sector_t *sector; + player_t *player; + + angle_t angle; + angle_t aim; + fixed_t cos; + fixed_t sin; + mobj_t *mobj; +} viewvars_t; + +extern viewvars_t *newview; + +// Interpolates the current view variables (r_state.h) against the selected view context in R_SetViewContext +void R_InterpolateView(fixed_t frac); +// Buffer the current new views into the old views. Call once after each real tic. +void R_UpdateViewInterpolation(void); +// Set the current view context (the viewvars pointed to by newview) +void R_SetViewContext(enum viewcontext_e _viewcontext); + +fixed_t R_LerpFixed(fixed_t from, fixed_t to, fixed_t frac); +INT32 R_LerpInt32(INT32 from, INT32 to, fixed_t frac); +UINT32 R_LerpUInt32(UINT32 from, UINT32 to, fixed_t frac); +angle_t R_LerpAngle(angle_t from, angle_t to, fixed_t frac); + +#endif diff --git a/src/r_main.c b/src/r_main.c index 13d2413fa..fe5daa00b 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -35,6 +35,7 @@ #include "r_portal.h" #include "r_main.h" #include "i_system.h" // I_GetPreciseTime +#include "r_fps.h" // Frame interpolation/uncapped #ifdef HWRENDER #include "hardware/hw_main.h" @@ -75,6 +76,8 @@ sector_t *viewsector; player_t *viewplayer; mobj_t *r_viewmobj; +fixed_t rendertimefrac; + // // precalculated math tables // @@ -96,6 +99,9 @@ lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; lighttable_t *scalelightfixed[MAXLIGHTSCALE]; lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; +// Frame interpolation/uncapped +tic_t prev_tics; + // Hack to support extra boom colormaps. extracolormap_t *extra_colormaps = NULL; @@ -164,6 +170,9 @@ consvar_t cv_drawdist_precip = CVAR_INIT ("drawdist_precip", "1024", CV_SAVE, dr //consvar_t cv_precipdensity = CVAR_INIT ("precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL); consvar_t cv_fov = CVAR_INIT ("fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange); +// Frame interpolation/uncapped +consvar_t cv_frameinterpolation = {"frameinterpolation", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; + // Okay, whoever said homremoval causes a performance hit should be shot. consvar_t cv_homremoval = CVAR_INIT ("homremoval", "No", CV_SAVE, homremoval_cons_t, NULL); @@ -1084,41 +1093,6 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y) // // R_SetupFrame // - -// recalc necessary stuff for mouseaiming -// slopes are already calculated for the full possible view (which is 4*viewheight). -// 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) -static void R_SetupFreelook(player_t *player, boolean skybox) -{ -#ifndef HWRENDER - (void)player; - (void)skybox; -#endif - - // clip it in the case we are looking a hardware 90 degrees full aiming - // (lmps, network and use F12...) - if (rendermode == render_soft -#ifdef HWRENDER - || (rendermode == render_opengl - && (cv_glshearing.value == 1 - || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) -#endif - ) - { - G_SoftwareClipAimingPitch((INT32 *)&aimingangle); - } - - centeryfrac = (viewheight/2)<climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode) @@ -1149,81 +1125,83 @@ void R_SetupFrame(player_t *player) else if (!chasecam) thiscam->chase = false; + newview->sky = false; + if (player->awayviewtics) { // cut-away view stuff r_viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN I_Assert(r_viewmobj != NULL); - viewz = r_viewmobj->z + 20*FRACUNIT; - aimingangle = player->awayviewaiming; - viewangle = r_viewmobj->angle; + newview->z = r_viewmobj->z + 20*FRACUNIT; + newview->aim = player->awayviewaiming; + newview->angle = r_viewmobj->angle; } else if (!player->spectator && chasecam) // use outside cam view { r_viewmobj = NULL; - viewz = thiscam->z + (thiscam->height>>1); - aimingangle = thiscam->aiming; - viewangle = thiscam->angle; + newview->z = thiscam->z + (thiscam->height>>1); + newview->aim = thiscam->aiming; + newview->angle = thiscam->angle; } else // use the player's eyes view { - viewz = player->viewz; + newview->z = player->viewz; r_viewmobj = player->mo; I_Assert(r_viewmobj != NULL); - aimingangle = player->aiming; - viewangle = r_viewmobj->angle; + newview->aim = player->aiming; + newview->angle = r_viewmobj->angle; if (!demoplayback && player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle; // WARNING: camera uses this - aimingangle = localaiming; + newview->angle = localangle; // WARNING: camera uses this + newview->aim = localaiming; } else if (player == &players[secondarydisplayplayer]) { - viewangle = localangle2; - aimingangle = localaiming2; + newview->angle = localangle2; + newview->aim = localaiming2; } } } - viewz += quake.z; + newview->z += quake.z; - viewplayer = player; + newview->player = player; if (chasecam && !player->awayviewtics && !player->spectator) { - viewx = thiscam->x; - viewy = thiscam->y; - viewx += quake.x; - viewy += quake.y; + newview->x = thiscam->x; + newview->y = thiscam->y; + newview->x += quake.x; + newview->y += quake.y; if (thiscam->subsector) - viewsector = thiscam->subsector->sector; + newview->sector = thiscam->subsector->sector; else - viewsector = R_PointInSubsector(viewx, viewy)->sector; + newview->sector = R_PointInSubsector(newview->x, newview->y)->sector; } else { - viewx = r_viewmobj->x; - viewy = r_viewmobj->y; - viewx += quake.x; - viewy += quake.y; + newview->x = r_viewmobj->x; + newview->y = r_viewmobj->y; + newview->x += quake.x; + newview->y += quake.y; if (r_viewmobj->subsector) - viewsector = r_viewmobj->subsector->sector; + newview->sector = r_viewmobj->subsector->sector; else - viewsector = R_PointInSubsector(viewx, viewy)->sector; + newview->sector = R_PointInSubsector(newview->x, newview->y)->sector; } - viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); - viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + // newview->sin = FINESINE(viewangle>>ANGLETOFINESHIFT); + // newview->cos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(player, false); + R_InterpolateView(cv_frameinterpolation.value == 1 ? rendertimefrac : FRACUNIT); } void R_SkyboxFrame(player_t *player) @@ -1232,11 +1210,18 @@ void R_SkyboxFrame(player_t *player) if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer]) + { thiscam = &camera2; + R_SetViewContext(VIEWCONTEXT_SKY2); + } else + { thiscam = &camera; + R_SetViewContext(VIEWCONTEXT_SKY1); + } // cut-away view stuff + newview->sky = true; r_viewmobj = skyboxmo[0]; #ifdef PARANOIA if (!r_viewmobj) @@ -1247,39 +1232,39 @@ void R_SkyboxFrame(player_t *player) #endif if (player->awayviewtics) { - aimingangle = player->awayviewaiming; - viewangle = player->awayviewmobj->angle; + newview->aim = player->awayviewaiming; + newview->angle = player->awayviewmobj->angle; } else if (thiscam->chase) { - aimingangle = thiscam->aiming; - viewangle = thiscam->angle; + newview->aim = thiscam->aiming; + newview->angle = thiscam->angle; } else { - aimingangle = player->aiming; - viewangle = player->mo->angle; + newview->aim = player->aiming; + newview->angle = player->mo->angle; if (!demoplayback && player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle; // WARNING: camera uses this - aimingangle = localaiming; + newview->angle = localangle; // WARNING: camera uses this + newview->aim = localaiming; } else if (player == &players[secondarydisplayplayer]) { - viewangle = localangle2; - aimingangle = localaiming2; + newview->angle = localangle2; + newview->aim = localaiming2; } } } - viewangle += r_viewmobj->angle; + newview->angle += r_viewmobj->angle; - viewplayer = player; + newview->player = player; - viewx = r_viewmobj->x; - viewy = r_viewmobj->y; - viewz = r_viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle! + newview->x = r_viewmobj->x; + newview->y = r_viewmobj->y; + newview->z = r_viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle! if (mapheaderinfo[gamemap-1]) { @@ -1321,46 +1306,46 @@ void R_SkyboxFrame(player_t *player) if (r_viewmobj->angle == 0) { - viewx += x; - viewy += y; + newview->x += x; + newview->y += y; } else if (r_viewmobj->angle == ANGLE_90) { - viewx -= y; - viewy += x; + newview->x -= y; + newview->y += x; } else if (r_viewmobj->angle == ANGLE_180) { - viewx -= x; - viewy -= y; + newview->x -= x; + newview->y -= y; } else if (r_viewmobj->angle == ANGLE_270) { - viewx += y; - viewy -= x; + newview->x += y; + newview->y -= x; } else { angle_t ang = r_viewmobj->angle>>ANGLETOFINESHIFT; - viewx += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang)); - viewy += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang)); + newview->x += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang)); + newview->y += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang)); } } if (mh->skybox_scalez > 0) - viewz += campos.z / mh->skybox_scalez; + newview->z += campos.z / mh->skybox_scalez; else if (mh->skybox_scalez < 0) - viewz += campos.z * -mh->skybox_scalez; + newview->z += campos.z * -mh->skybox_scalez; } if (r_viewmobj->subsector) - viewsector = r_viewmobj->subsector->sector; + newview->sector = r_viewmobj->subsector->sector; else - viewsector = R_PointInSubsector(viewx, viewy)->sector; + newview->sector = R_PointInSubsector(newview->x, newview->y)->sector; - viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); - viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + // newview->sin = FINESINE(viewangle>>ANGLETOFINESHIFT); + // newview->cos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(player, true); + R_InterpolateView(cv_frameinterpolation.value == 1 ? rendertimefrac : FRACUNIT); } boolean R_ViewpointHasChasecam(player_t *player) @@ -1642,4 +1627,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_maxportals); CV_RegisterVar(&cv_movebob); + + // Frame interpolation/uncapped + CV_RegisterVar(&cv_frameinterpolation); } diff --git a/src/r_main.h b/src/r_main.h index c0edb31b3..860bfa920 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -35,6 +35,8 @@ extern fixed_t fovtan; extern size_t validcount, linecount, loopcount, framecount; +extern fixed_t rendertimefrac; + // // Lighting LUT. // Used for z-depth cuing per column/row, @@ -114,6 +116,10 @@ extern consvar_t cv_fov; extern consvar_t cv_skybox; extern consvar_t cv_tailspickup; +// Frame interpolation (uncapped framerate) +extern tic_t prev_tics; +extern consvar_t cv_frameinterpolation; + // Called by startup code. void R_Init(void); diff --git a/src/r_things.c b/src/r_things.c index db4263a6a..55e84d6b7 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1480,9 +1480,34 @@ static void R_ProjectSprite(mobj_t *thing) INT32 rollangle = 0; #endif + // uncapped/interpolation + fixed_t interpx = thing->x; + fixed_t interpy = thing->y; + fixed_t interpz = thing->z; + angle_t interpangle = thing->angle; + + // use player drawangle if player + if (thing->player) interpangle = thing->player->drawangle; + + // do interpolation + if (cv_frameinterpolation.value == 1 && !paused) + { + interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); + interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); + interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); + if (thing->player) + { + interpangle = thing->player->drawangle; + } + else + { + interpangle = thing->angle; + } + } + // transform the origin point - tr_x = thing->x - viewx; - tr_y = thing->y - viewy; + tr_x = interpx - viewx; + tr_y = interpy - viewy; basetz = tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance @@ -1559,7 +1584,7 @@ static void R_ProjectSprite(mobj_t *thing) if (sprframe->rotate != SRF_SINGLE || papersprite) { - ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle); + ang = R_PointToAngle (interpx, interpy) - interpangle; if (mirrored) ang = InvAngle(ang); } @@ -1574,7 +1599,7 @@ static void R_ProjectSprite(mobj_t *thing) else { // choose a different rotation based on player view - //ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + //ang = R_PointToAngle (interpx, interpy) - interpangle; if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right rot = 6; // F7 slot @@ -1791,12 +1816,17 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t linkscale; thing = thing->tracer; + if (cv_frameinterpolation.value == 1 && !paused) + { + interpx = thing->old_x + FixedMul(thing->x - thing->old_x, rendertimefrac); + interpy = thing->old_y + FixedMul(thing->y - thing->old_y, rendertimefrac); + } if (! R_ThingVisible(thing)) return; - tr_x = (thing->x + sort_x) - viewx; - tr_y = (thing->y + sort_y) - viewy; + tr_x = (interpx + sort_x) - viewx; + tr_y = (interpy + sort_y) - viewy; tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); linkscale = FixedDiv(projectiony, tz); @@ -1832,7 +1862,7 @@ static void R_ProjectSprite(mobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; - if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0) + if (P_PointOnLineSide(interpx, interpy, portalclipline) != 0) return; } @@ -1921,12 +1951,12 @@ static void R_ProjectSprite(mobj_t *thing) // When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned. // sprite height - sprite topoffset is the proper inverse of the vertical offset, of course. // remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes! - gz = oldthing->z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); + gz = interpz + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale)); } else { - gzt = oldthing->z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); + gzt = interpz + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); gz = gzt - FixedMul(spr_height, FixedMul(spriteyscale, this_scale)); } } @@ -1945,7 +1975,7 @@ static void R_ProjectSprite(mobj_t *thing) // R_GetPlaneLight won't work on sloped lights! for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { - fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], thing->x, thing->y); + fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interpx, interpy); if (h <= top) { light = lightnum - 1; break; @@ -1995,12 +2025,12 @@ static void R_ProjectSprite(mobj_t *thing) vis->sortscale = sortscale; vis->sortsplat = sortsplat; vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15 - vis->gx = thing->x; - vis->gy = thing->y; + vis->gx = interpx; + vis->gy = interpy; vis->gz = gz; vis->gzt = gzt; vis->thingheight = thing->height; - vis->pz = thing->z; + vis->pz = interpz; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = FixedDiv(gzt - viewz, spriteyscale); vis->scalestep = scalestep; @@ -2139,9 +2169,22 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) //SoM: 3/17/2000 fixed_t gz, gzt; + // uncapped/interpolation + fixed_t interpx = thing->x; + fixed_t interpy = thing->y; + fixed_t interpz = thing->z; + + // do interpolation + if (cv_frameinterpolation.value == 1 && !paused) + { + interpx = thing->old_x + FixedMul(rendertimefrac, thing->x - thing->old_x); + interpy = thing->old_y + FixedMul(rendertimefrac, thing->y - thing->old_y); + interpz = thing->old_z + FixedMul(rendertimefrac, thing->z - thing->old_z); + } + // transform the origin point - tr_x = thing->x - viewx; - tr_y = thing->y - viewy; + tr_x = interpx - viewx; + tr_y = interpy - viewy; tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance @@ -2205,13 +2248,13 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; - if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0) + if (P_PointOnLineSide(interpx, interpy, portalclipline) != 0) return; } //SoM: 3/17/2000: Disregard sprites that are out of view.. - gzt = thing->z + spritecachedinfo[lump].topoffset; + gzt = interpz + spritecachedinfo[lump].topoffset; gz = gzt - spritecachedinfo[lump].height; if (thing->subsector->sector->cullheight) @@ -2224,12 +2267,12 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis = R_NewVisSprite(); vis->scale = vis->sortscale = yscale; //<dispoffset = 0; // Monster Iestyn: 23/11/15 - vis->gx = thing->x; - vis->gy = thing->y; + vis->gx = interpx; + vis->gy = interpy; vis->gz = gz; vis->gzt = gzt; vis->thingheight = 4*FRACUNIT; - vis->pz = thing->z; + vis->pz = interpz; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = 0; diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 9de632734..2d99f5c1f 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -185,7 +185,7 @@ static char returnWadPath[256]; #include "../m_argv.h" -#include "../m_menu.h" +#include "../r_main.h" // Frame interpolation/uncapped #ifdef MAC_ALERT #include "macosx/mac_alert.h" @@ -2144,17 +2144,27 @@ static Uint64 timer_frequency; static double tic_frequency; static Uint64 tic_epoch; +static double elapsed_tics; + +static void UpdateElapsedTics(void) +{ + const Uint64 now = SDL_GetPerformanceCounter(); + + elapsed_tics += (now - tic_epoch) / tic_frequency; + tic_epoch = now; // moving epoch +} tic_t I_GetTime(void) { - static double elapsed; + UpdateElapsedTics(); + return (tic_t) floor(elapsed_tics); +} - const Uint64 now = SDL_GetPerformanceCounter(); - - elapsed += (now - tic_epoch) / tic_frequency; - tic_epoch = now; // moving epoch - - return (tic_t)elapsed; +fixed_t I_GetTimeFrac(void) +{ + UpdateElapsedTics(); + + return FLOAT_TO_FIXED((float) (elapsed_tics - floor(elapsed_tics))); } precise_t I_GetPreciseTime(void) @@ -2182,6 +2192,7 @@ void I_StartupTimer(void) tic_epoch = SDL_GetPerformanceCounter(); tic_frequency = timer_frequency / (double)NEWTICRATE; + elapsed_tics = 0.0; } void I_Sleep(void) diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index ff443935f..393b913da 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -45,6 +45,7 @@ #include "../d_main.h" #include "../m_argv.h" +#include "../m_fixed.h" #include "../w_wad.h" #include "../z_zone.h" @@ -261,6 +262,11 @@ tic_t I_GetTime(void) return newtics; } +fixed_t I_GetTimeFrac(void) +{ + return 0; +} + void I_Sleep(void) { if (cv_sleep.value != -1)