diff --git a/src/Sourcefile b/src/Sourcefile index f71259092..9c7960081 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -20,6 +20,7 @@ command.c console.c font.c hu_stuff.c +i_time.c y_inter.c st_stuff.c m_aatree.c @@ -97,6 +98,7 @@ lua_taglib.c lua_polyobjlib.c lua_blockmaplib.c lua_hudlib.c +lua_hudlib_drawlist.c k_kart.c k_respawn.c k_collide.c @@ -115,4 +117,4 @@ k_boss.c k_hud.c k_terrain.c k_brightmap.c -k_director.c \ No newline at end of file +k_director.c diff --git a/src/android/i_system.c b/src/android/i_system.c index d8b81dcec..4ce2cf260 100644 --- a/src/android/i_system.c +++ b/src/android/i_system.c @@ -82,20 +82,18 @@ INT64 current_time_in_ps() { return (t.tv_sec * (INT64)1000000) + t.tv_usec; } -tic_t I_GetTime(void) +void I_Sleep(UINT32 ms){} + +precise_t I_GetPreciseTime(void) { - INT64 since_start = current_time_in_ps() - start_time; - return (since_start*TICRATE)/1000000; + return 0; } -fixed_t I_GetTimeFrac(void) +UINT64 I_GetPrecisePrecision(void) { - //stub - return 0; + return 1000000; } -void I_Sleep(void){} - void I_GetEvent(void){} void I_OsPolling(void){} diff --git a/src/command.c b/src/command.c index ec1432419..a0f2967bf 100644 --- a/src/command.c +++ b/src/command.c @@ -2010,9 +2010,10 @@ void CV_AddValue(consvar_t *var, INT32 increment) { increment = 0; currentindice = max; + break; // The value we definitely want, stop here. } else if (var->PossibleValue[max].value == var->value) - currentindice = max; + currentindice = max; // The value we maybe want. } if (increment) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7f6e6f287..21aab6c58 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -15,6 +15,7 @@ #include //for unlink #endif +#include "i_time.h" #include "i_net.h" #include "i_system.h" #include "i_video.h" @@ -1947,7 +1948,10 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic #endif } else - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } return true; } diff --git a/src/d_main.c b/src/d_main.c index a78f857fb..d6996b5a8 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -40,6 +40,7 @@ #include "hu_stuff.h" #include "i_sound.h" #include "i_system.h" +#include "i_time.h" #include "i_threads.h" #include "i_video.h" #include "m_argv.h" @@ -455,6 +456,8 @@ static void D_Display(void) { if (!automapactive && !dedicated && cv_renderview.value) { + R_ApplyLevelInterpolators(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); + viewwindowy = 0; viewwindowx = 0; @@ -530,6 +533,7 @@ static void D_Display(void) } ps_rendercalltime = I_GetPreciseTime() - ps_rendercalltime; + R_RestoreLevelInterpolators(); } if (lastdraw) @@ -710,9 +714,11 @@ tic_t rendergametic; void D_SRB2Loop(void) { - tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; - boolean ticked; - boolean interp; + tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS; + double deltatics = 0.0; + double deltasecs = 0.0; + + boolean interp = false; boolean doDisplay = false; if (dedicated) @@ -725,6 +731,7 @@ void D_SRB2Loop(void) I_DoStartupMouse(); #endif + I_UpdateTime(cv_timescale.value); oldentertics = I_GetTime(); // end of loading screen: CONS_Printf() will no more call FinishUpdate() @@ -766,6 +773,19 @@ void D_SRB2Loop(void) for (;;) { + // capbudget is the minimum precise_t duration of a single loop iteration + precise_t capbudget; + precise_t enterprecise = I_GetPreciseTime(); + precise_t finishprecise = enterprecise; + + { + // Casting the return value of a function is bad practice (apparently) + double budget = round((1.0 / R_GetFramerateCap()) * I_GetPrecisePrecision()); + capbudget = (precise_t) budget; + } + + I_UpdateTime(cv_timescale.value); + if (lastwipetic) { oldentertics = lastwipetic; @@ -777,8 +797,6 @@ void D_SRB2Loop(void) realtics = entertic - oldentertics; oldentertics = entertic; - refreshdirmenu = 0; // not sure where to put this, here as good as any? - if (demo.playback && gamestate == GS_LEVEL) { // Nicer place to put this. @@ -791,16 +809,8 @@ void D_SRB2Loop(void) debugload--; #endif - interp = R_UsingFrameInterpolation(); + interp = R_UsingFrameInterpolation() && !dedicated; doDisplay = false; - ticked = false; - - if (!realtics && !singletics && !interp) - { - // Non-interp sleep - I_Sleep(); - continue; - } #ifdef HW3SOUND HW3S_BeginFrameUpdate(); @@ -814,12 +824,12 @@ void D_SRB2Loop(void) realtics = 1; // process tics (but maybe not if realtic == 0) - ticked = TryRunTics(realtics); + TryRunTics(realtics); if (lastdraw || singletics || gametic > rendergametic) { rendergametic = gametic; - rendertimeout = entertic+TICRATE/17; + rendertimeout = entertic + TICRATE/17; doDisplay = true; } @@ -827,63 +837,26 @@ void D_SRB2Loop(void) { doDisplay = true; } + + renderisnewtic = true; + } + else + { + renderisnewtic = false; } if (interp) { - static float tictime = 0.0f; - static float prevtime = 0.0f; - float entertime = I_GetTimeFrac(); - - fixed_t entertimefrac = FRACUNIT; - - if (ticked) - { - tictime = entertime; - } - - // Handle interp sleep / framerate cap here. - // TryRunTics needs ran if possible to prevent lagged map changes, - // (and if that runs, the code above needs to also run) - // so this is done here after TryRunTics. - if (D_CheckFrameCap()) - { - continue; - } + renderdeltatics = FLOAT_TO_FIXED(deltatics); if (!(paused || P_AutoPause())) { -#if 0 - CONS_Printf("prevtime = %f\n", prevtime); - CONS_Printf("entertime = %f\n", entertime); - CONS_Printf("tictime = %f\n", tictime); - CONS_Printf("entertime - prevtime = %f\n", entertime - prevtime); - CONS_Printf("entertime - tictime = %f\n", entertime - tictime); - CONS_Printf("========\n"); -#endif - - if (entertime - prevtime >= 1.0f) - { - // Lagged for more frames than a gametic... - // No need for interpolation. - entertimefrac = FRACUNIT; - } - else - { - entertimefrac = min(FRACUNIT, FLOAT_TO_FIXED(entertime - tictime)); - } - - // renderdeltatics is a bit awkard to evaluate, since the system time interface is whole tic-based - renderdeltatics = realtics * FRACUNIT; - if (entertimefrac > rendertimefrac) - renderdeltatics += entertimefrac - rendertimefrac; - else - renderdeltatics -= rendertimefrac - entertimefrac; - - rendertimefrac = entertimefrac; + rendertimefrac = g_time.timefrac; + } + else + { + rendertimefrac = FRACUNIT; } - - prevtime = entertime; } else { @@ -896,9 +869,10 @@ void D_SRB2Loop(void) D_Display(); } + // Only take screenshots after drawing. if (moviemode) M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. + if (takescreenshot) M_DoScreenShot(); // consoleplayer -> displayplayers (hear sounds from viewpoint) @@ -918,10 +892,20 @@ void D_SRB2Loop(void) } #endif - // Moved to here from I_FinishUpdate. - // It doesn't track fades properly anymore by being here (might be easy fix), - // but it's a little more accurate for actual game logic when its here. - SCR_CalculateFPS(); + // Fully completed frame made. + finishprecise = I_GetPreciseTime(); + if (!singletics) + { + INT64 elapsed = (INT64)(finishprecise - enterprecise); + if (elapsed > 0 && (INT64)capbudget > elapsed) + { + I_SleepDuration(capbudget - (finishprecise - enterprecise)); + } + } + // Capture the time once more to get the real delta time. + finishprecise = I_GetPreciseTime(); + deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision(); + deltatics = deltasecs * NEWTICRATE; } } @@ -1404,8 +1388,8 @@ void D_SRB2Main(void) //---------------------------------------------------- READY TIME // we need to check for dedicated before initialization of some subsystems - CONS_Printf("I_StartupTimer()...\n"); - I_StartupTimer(); + CONS_Printf("I_InitializeTime()...\n"); + I_InitializeTime(); CON_SetLoadingProgress(LOADED_ISTARTUPTIMER); // Make backups of some SOCcable tables. diff --git a/src/d_net.c b/src/d_net.c index f28ba72b8..1c81fe3f3 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -18,6 +18,7 @@ #include "doomdef.h" #include "g_game.h" +#include "i_time.h" #include "i_net.h" #include "i_system.h" #include "m_argv.h" @@ -618,7 +619,10 @@ void Net_WaitAllAckReceived(UINT32 timeout) while (timeout > I_GetTime() && !Net_AllAcksReceived()) { while (tictac == I_GetTime()) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } tictac = I_GetTime(); HGetPacket(); Net_AckTicker(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 51b6b97d6..3176622e9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -16,6 +16,7 @@ #include "console.h" #include "command.h" +#include "i_time.h" #include "i_system.h" #include "g_game.h" #include "hu_stuff.h" diff --git a/src/d_netfil.c b/src/d_netfil.c index 3c269f63a..73e179a6a 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -43,6 +43,7 @@ #include "doomstat.h" #include "d_main.h" #include "g_game.h" +#include "i_time.h" #include "i_net.h" #include "i_system.h" #include "m_argv.h" diff --git a/src/d_player.h b/src/d_player.h index 55c2857d5..ecac462ec 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -343,6 +343,7 @@ typedef struct player_s // fun thing for player sprite angle_t drawangle; angle_t old_drawangle; // interp + angle_t old_drawangle2; // Bit flags. // See pflags_t, above. diff --git a/src/doomtype.h b/src/doomtype.h index c6cde7b6b..7037c41cc 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -402,9 +402,7 @@ unset_bit_array (bitarray_t * const array, const int value) array[value >> 3] &= ~(1<<(value & 7)); } -#ifdef HAVE_SDL typedef UINT64 precise_t; -#endif #define intsign(n) \ ((n) < 0 ? -1 : (n) > 0 ? 1 : 0) diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c index f3d0bc5e8..34fb587ab 100644 --- a/src/dummy/i_system.c +++ b/src/dummy/i_system.c @@ -11,18 +11,16 @@ UINT32 I_GetFreeMem(UINT32 *total) return 0; } -tic_t I_GetTime(void) -{ +void I_Sleep(UINT32 ms){} + +precise_t I_GetPreciseTime(void) { return 0; } -fixed_t I_GetTimeFrac(void) -{ - return 0; +UINT64 I_GetPrecisePrecision(void) { + return 1000000; } -void I_Sleep(void){} - void I_GetEvent(void){} void I_OsPolling(void){} diff --git a/src/f_finale.c b/src/f_finale.c index e33a379d7..8acafaf16 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -20,6 +20,7 @@ #include "hu_stuff.h" #include "r_local.h" #include "s_sound.h" +#include "i_time.h" #include "i_video.h" #include "v_video.h" #include "w_wad.h" @@ -58,8 +59,6 @@ static tic_t stoptimer; static boolean keypressed = false; -static tic_t xscrolltimer; -static tic_t yscrolltimer; static INT32 menuanimtimer; // Title screen: background animation timing mobj_t *titlemapcameraref = NULL; @@ -158,6 +157,8 @@ static tic_t cutscene_lasttextwrite = 0; // STJR Intro char stjrintro[9] = "STJRI000"; +static huddrawlist_h luahuddrawlist_title; + // // This alters the text string cutscene_disptext. // Use the typical string drawing functions to display it. @@ -361,9 +362,6 @@ static void F_IntroDrawScene(void) W_UnlockCachedPatch(background); - if (animtimer) - animtimer--; - V_DrawString(cx, cy, 0, cutscene_disptext); } @@ -392,7 +390,10 @@ void F_IntroDrawer(void) while (quittime > nowtime) { while (!((nowtime = I_GetTime()) - lasttime)) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } lasttime = nowtime; I_OsPolling(); @@ -462,6 +463,9 @@ void F_IntroTicker(void) // check for skipping if (keypressed) keypressed = false; + + if (animtimer > 0) + animtimer--; } // @@ -1706,6 +1710,9 @@ void F_InitMenuPresValues(void) //M_SetMenuCurBackground((gamestate == GS_TIMEATTACK) ? "RECATTBG" : "TITLESKY"); //M_SetMenuCurFadeValue(16); //M_SetMenuCurTitlePics(); + + LUA_HUD_DestroyDrawList(luahuddrawlist_title); + luahuddrawlist_title = LUA_HUD_CreateDrawList(); } // @@ -1721,6 +1728,7 @@ void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname) INT32 pw, ph; // scaled by dupz patch_t *pat; INT32 i, j; + fixed_t fracmenuanimtimer, xscrolltimer, yscrolltimer; if (rendermode == render_none) return; @@ -1747,12 +1755,13 @@ void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname) tilex = max(FixedCeil(FixedDiv(vid.width, pw)) >> FRACBITS, 1)+2; // one tile on both sides of center tiley = max(FixedCeil(FixedDiv(vid.height, ph)) >> FRACBITS, 1)+2; - xscrolltimer = ((menuanimtimer*scrollxspeed)/16 + patwidth*xneg) % (patwidth); - yscrolltimer = ((menuanimtimer*scrollyspeed)/16 + patheight*yneg) % (patheight); + fracmenuanimtimer = (menuanimtimer * FRACUNIT) - (FRACUNIT - rendertimefrac); + xscrolltimer = ((fracmenuanimtimer*scrollxspeed)/16 + patwidth*xneg*FRACUNIT) % (patwidth * FRACUNIT); + yscrolltimer = ((fracmenuanimtimer*scrollyspeed)/16 + patheight*yneg*FRACUNIT) % (patheight * FRACUNIT); // coordinate offsets - xscrolled = xscrolltimer * dupz; - yscrolled = yscrolltimer * dupz; + xscrolled = FixedInt(xscrolltimer * dupz); + yscrolled = FixedInt(yscrolltimer * dupz); for (x = (xispos) ? -pw*(tilex-1)+pw : 0, i = 0; i < tilex; @@ -1997,7 +2006,12 @@ void F_TitleScreenDrawer(void) } luahook: - LUAh_TitleHUD(); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_title); + LUAh_TitleHUD(luahuddrawlist_title); + } + LUA_HUD_DrawList(luahuddrawlist_title); } // separate animation timer for backgrounds, since we also count @@ -2260,6 +2274,7 @@ static INT32 textxpos, textypos; static boolean dofadenow = false, cutsceneover = false; static boolean runningprecutscene = false, precutresetplayer = false; + static void F_AdvanceToNextScene(void) { // Don't increment until after endcutscene check @@ -2269,6 +2284,7 @@ static void F_AdvanceToNextScene(void) F_EndCutScene(); return; } + ++scenenum; timetonext = 0; @@ -2284,7 +2300,6 @@ static void F_AdvanceToNextScene(void) cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0); // Fade to the next - dofadenow = true; F_NewCutscene(cutscenes[cutnum]->scene[scenenum].text); picnum = 0; @@ -2417,8 +2432,6 @@ void F_CutsceneTicker(void) finalecount++; cutscene_boostspeed = 0; - dofadenow = false; - for (i = 0; i < MAXPLAYERS; i++) { if (netgame && i != serverplayer && !IsPlayerAdmin(i)) diff --git a/src/f_wipe.c b/src/f_wipe.c index db343d050..ca4f7be50 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -23,6 +23,7 @@ #include "w_wad.h" #include "z_zone.h" +#include "i_time.h" #include "i_system.h" #include "i_threads.h" #include "m_menu.h" @@ -477,7 +478,10 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean r // wait loop while (!((nowtime = I_GetTime()) - lastwipetic)) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } lastwipetic = nowtime; #ifdef HWRENDER diff --git a/src/g_demo.c b/src/g_demo.c index c53166e8e..6521c0b40 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -17,6 +17,7 @@ #include "d_player.h" #include "d_clisrv.h" #include "p_setup.h" +#include "i_time.h" #include "i_system.h" #include "m_random.h" #include "p_local.h" diff --git a/src/g_game.c b/src/g_game.c index f2b0728a7..2af8cabe0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -21,6 +21,7 @@ #include "filesrch.h" // for refreshdirmenu #include "p_setup.h" #include "p_saveg.h" +#include "i_time.h" #include "i_system.h" #include "am_map.h" #include "m_random.h" @@ -1336,7 +1337,10 @@ void G_PreLevelTitleCard(void) M_DoScreenShot(); while (!((nowtime = I_GetTime()) - lasttime)) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } lasttime = nowtime; } #endif @@ -1928,7 +1932,6 @@ void G_Ticker(boolean run) F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); - R_UpdateViewInterpolation(); break; @@ -1987,10 +1990,7 @@ void G_Ticker(boolean run) case GS_TITLESCREEN: if (titlemapinaction) - { P_Ticker(run); - R_UpdateViewInterpolation(); - } F_TitleScreenTicker(run); break; diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index 6ef96f68e..808afd701 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -51,7 +51,7 @@ EXPORT void HWRAPI(ClearMipMapCache) (void); EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value); //Hurdler: added for new development -EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface); +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface); EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *stransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index af54a350a..1fabe6068 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -24,6 +24,7 @@ #include "../v_video.h" #include "../p_local.h" #include "../p_setup.h" +#include "../r_fps.h" #include "../r_local.h" #include "../r_patch.h" #include "../r_picformats.h" @@ -3051,6 +3052,7 @@ static void HWR_Subsector(size_t num) INT32 light = 0; extracolormap_t *floorcolormap; extracolormap_t *ceilingcolormap; + ffloor_t *rover; #ifdef PARANOIA //no risk while developing, enough debugging nights! if (num >= addsubsector) @@ -3108,7 +3110,22 @@ static void HWR_Subsector(size_t num) if (gl_frontsector->ffloors) { - if (gl_frontsector->moved) + boolean anyMoved = gl_frontsector->moved; + + if (anyMoved == false) + { + for (rover = gl_frontsector->ffloors; rover; rover = rover->next) + { + sector_t *controlSec = §ors[rover->secnum]; + if (controlSec->moved == true) + { + anyMoved = true; + break; + } + } + } + + if (anyMoved == true) { gl_frontsector->numlights = sub->sector->numlights = 0; R_Prep3DFloors(gl_frontsector); @@ -3189,7 +3206,6 @@ static void HWR_Subsector(size_t num) if (gl_frontsector->ffloors) { /// \todo fix light, xoffs, yoffs, extracolormap ? - ffloor_t * rover; for (rover = gl_frontsector->ffloors; rover; rover = rover->next) { @@ -3703,9 +3719,17 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) fixed_t slopez; pslope_t *groundslope; - fixed_t interpx = R_InterpolateFixed(thing->old_x, thing->x); - fixed_t interpy = R_InterpolateFixed(thing->old_y, thing->y); - fixed_t interpz = R_InterpolateFixed(thing->old_z, thing->z); + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) @@ -3717,17 +3741,17 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) mul = -mul; } - interpx += FixedMul(thing->momx, mul); - interpy += FixedMul(thing->momy, mul); - interpz += FixedMul(thing->momz, mul); + interp.x += FixedMul(thing->momx, mul); + interp.y += FixedMul(thing->momy, mul); + interp.z += FixedMul(thing->momz, mul); } // sprite offset - interpx += thing->sprxoff; - interpy += thing->spryoff; - interpz += thing->sprzoff; + interp.x += thing->sprxoff; + interp.y += thing->spryoff; + interp.z += thing->sprzoff; - groundz = R_GetShadowZ(thing, &groundslope, interpx, interpy, interpz); + groundz = R_GetShadowZ(thing, &groundslope); gpatch = (patch_t *)W_CachePatchName("DSHADOW", PU_SPRITE); if (!(gpatch && ((GLPatch_t *)gpatch->hardware)->mipmap->format)) return; @@ -3738,8 +3762,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) ph = (float)gpatch->height; fscale = FIXED_TO_FLOAT(scalemul); - fx = FIXED_TO_FLOAT(interpx); - fy = FIXED_TO_FLOAT(interpy); + fx = FIXED_TO_FLOAT(interp.x); + fy = FIXED_TO_FLOAT(interp.y); if (fscale > 0.0) { @@ -5146,33 +5170,22 @@ static void HWR_ProjectSprite(mobj_t *thing) #endif // uncapped/interpolation - fixed_t interpx; - fixed_t interpy; - fixed_t interpz; - angle_t interpangle; + interpmobjstate_t interp = {0}; if (!thing) return; - if (thing->spritexscale < 1 || thing->spriteyscale < 1) - return; - - dispoffset = thing->info->dispoffset; - - interpx = R_InterpolateFixed(thing->old_x, thing->x); - interpy = R_InterpolateFixed(thing->old_y, thing->y); - interpz = R_InterpolateFixed(thing->old_z, thing->z); - interpangle = ANGLE_MAX; - - if (thing->player) + if (R_UsingFrameInterpolation() && !paused) { - interpangle = R_InterpolateAngle(thing->player->old_drawangle, thing->player->drawangle); + R_InterpolateMobjState(thing, rendertimefrac, &interp); } else { - interpangle = R_InterpolateAngle(thing->old_angle, thing->angle); + R_InterpolateMobjState(thing, FRACUNIT, &interp); } + dispoffset = thing->info->dispoffset; + // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) { @@ -5183,23 +5196,26 @@ static void HWR_ProjectSprite(mobj_t *thing) mul = -mul; } - interpx += FixedMul(thing->momx, mul); - interpy += FixedMul(thing->momy, mul); - interpz += FixedMul(thing->momz, mul); + interp.x += FixedMul(thing->momx, mul); + interp.y += FixedMul(thing->momy, mul); + interp.z += FixedMul(thing->momz, mul); } // sprite offset - interpx += thing->sprxoff; - interpy += thing->spryoff; - interpz += thing->sprzoff; + interp.x += thing->sprxoff; + interp.y += thing->spryoff; + interp.z += thing->sprzoff; - this_scale = FIXED_TO_FLOAT(thing->scale); - spritexscale = FIXED_TO_FLOAT(thing->spritexscale); - spriteyscale = FIXED_TO_FLOAT(thing->spriteyscale); + if (interp.spritexscale < 1 || interp.spriteyscale < 1) + return; + + this_scale = FIXED_TO_FLOAT(interp.scale); + spritexscale = FIXED_TO_FLOAT(interp.spritexscale); + spriteyscale = FIXED_TO_FLOAT(interp.spriteyscale); // transform the origin point - tr_x = FIXED_TO_FLOAT(interpx) - gl_viewx; - tr_y = FIXED_TO_FLOAT(interpy) - gl_viewy; + tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx; + tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy; // rotation around vertical axis tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin); @@ -5222,8 +5238,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(interpx); - tr_y = FIXED_TO_FLOAT(interpy); + tr_x = FIXED_TO_FLOAT(interp.x); + tr_y = FIXED_TO_FLOAT(interp.y); // decide which patch to use for sprite relative to player #ifdef RANGECHECK @@ -5271,7 +5287,7 @@ static void HWR_ProjectSprite(mobj_t *thing) I_Error("sprframes NULL for sprite %d\n", thing->sprite); #endif - ang = R_PointToAngle (interpx, interpy) - interpangle; + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if (mirrored) ang = InvAngle(ang); @@ -5342,8 +5358,8 @@ static void HWR_ProjectSprite(mobj_t *thing) if (thing->renderflags & RF_ABSOLUTEOFFSETS) { - spr_offset = thing->spritexoffset; - spr_topoffset = thing->spriteyoffset; + spr_offset = interp.spritexoffset; + spr_topoffset = interp.spriteyoffset; } else { @@ -5352,14 +5368,14 @@ static void HWR_ProjectSprite(mobj_t *thing) if ((thing->renderflags & RF_FLIPOFFSETS) && flip) flipoffset = -1; - spr_offset += thing->spritexoffset * flipoffset; - spr_topoffset += thing->spriteyoffset * flipoffset; + spr_offset += interp.spritexoffset * flipoffset; + spr_topoffset += interp.spriteyoffset * flipoffset; } if (papersprite) { - rightsin = FIXED_TO_FLOAT(FINESINE((interpangle)>>ANGLETOFINESHIFT)); - rightcos = FIXED_TO_FLOAT(FINECOSINE((interpangle)>>ANGLETOFINESHIFT)); + rightsin = FIXED_TO_FLOAT(FINESINE(interp.angle >> ANGLETOFINESHIFT)); + rightcos = FIXED_TO_FLOAT(FINECOSINE(interp.angle >> ANGLETOFINESHIFT)); } else { @@ -5372,14 +5388,24 @@ static void HWR_ProjectSprite(mobj_t *thing) if (thing->renderflags & RF_SHADOWEFFECTS) { mobj_t *caster = thing->target; + interpmobjstate_t casterinterp = {}; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(caster, rendertimefrac, &casterinterp); + } + else + { + R_InterpolateMobjState(caster, FRACUNIT, &casterinterp); + } if (caster && !P_MobjWasRemoved(caster)) { - fixed_t groundz = R_GetShadowZ(thing, NULL, interpx, interpy, interpz); - fixed_t floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + caster->z - groundz); + fixed_t groundz = R_GetShadowZ(thing, NULL); + fixed_t floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + casterinterp.z - groundz); shadowheight = FIXED_TO_FLOAT(floordiff); - shadowscale = FIXED_TO_FLOAT(FixedMul(FRACUNIT - floordiff/640, caster->scale)); + shadowscale = FIXED_TO_FLOAT(FixedMul(FRACUNIT - floordiff/640, casterinterp.scale)); if (splat) spritexscale *= shadowscale; @@ -5420,12 +5446,12 @@ static void HWR_ProjectSprite(mobj_t *thing) if (vflip) { - gz = FIXED_TO_FLOAT(interpz + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); + gz = FIXED_TO_FLOAT(interp.z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale); } else { - gzt = FIXED_TO_FLOAT(interpz) + (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); + gzt = FIXED_TO_FLOAT(interp.z) + (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); gz = gzt - (FIXED_TO_FLOAT(spr_height) * this_yscale); } @@ -5444,24 +5470,35 @@ static void HWR_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { if (gl_viewz < FIXED_TO_FLOAT(sectors[phs].floorheight) ? - FIXED_TO_FLOAT(interpz) >= FIXED_TO_FLOAT(sectors[heightsec].floorheight) : + FIXED_TO_FLOAT(interp.z) >= FIXED_TO_FLOAT(sectors[heightsec].floorheight) : gzt < FIXED_TO_FLOAT(sectors[heightsec].floorheight)) return; if (gl_viewz > FIXED_TO_FLOAT(sectors[phs].ceilingheight) ? gzt < FIXED_TO_FLOAT(sectors[heightsec].ceilingheight) && gl_viewz >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight) : - FIXED_TO_FLOAT(interpz) >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight)) + FIXED_TO_FLOAT(interp.z) >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight)) return; } if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) { + interpmobjstate_t tracer_interp = {}; + if (! R_ThingVisible(thing->tracer)) return; + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing->tracer, rendertimefrac, &tracer_interp); + } + else + { + R_InterpolateMobjState(thing->tracer, FRACUNIT, &tracer_interp); + } + // calculate tz for tracer, same way it is calculated for this sprite // transform the origin point - tr_x = FIXED_TO_FLOAT(thing->tracer->x) - gl_viewx; - tr_y = FIXED_TO_FLOAT(thing->tracer->y) - gl_viewy; + tr_x = FIXED_TO_FLOAT(tracer_interp.x) - gl_viewx; + tr_y = FIXED_TO_FLOAT(tracer_interp.y) - gl_viewy; // rotation around vertical axis tracertz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin); @@ -5588,21 +5625,25 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) unsigned rot = 0; UINT8 flip; - // uncapped/interpolation - fixed_t interpx; - fixed_t interpy; - fixed_t interpz; - if (!thing) return; - interpx = R_InterpolateFixed(thing->old_x, thing->x); - interpy = R_InterpolateFixed(thing->old_y, thing->y); - interpz = R_InterpolateFixed(thing->old_z, thing->z); + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolatePrecipMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolatePrecipMobjState(thing, FRACUNIT, &interp); + } // transform the origin point - tr_x = FIXED_TO_FLOAT(interpx) - gl_viewx; - tr_y = FIXED_TO_FLOAT(interpy) - gl_viewy; + tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx; + tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy; // rotation around vertical axis tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin); @@ -5611,8 +5652,8 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) if (tz < ZCLIP_PLANE) return; - tr_x = FIXED_TO_FLOAT(interpx); - tr_y = FIXED_TO_FLOAT(interpy); + tr_x = FIXED_TO_FLOAT(interp.x); + tr_y = FIXED_TO_FLOAT(interp.y); // decide which patch to use for sprite relative to player if ((unsigned)thing->sprite >= numsprites) @@ -5679,7 +5720,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) #endif // set top/bottom coords - vis->gzt = FIXED_TO_FLOAT(interpz + spritecachedinfo[lumpoff].topoffset); + vis->gzt = FIXED_TO_FLOAT(interp.z + spritecachedinfo[lumpoff].topoffset); vis->gz = vis->gzt - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height); vis->precip = true; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 896ef7b8c..e9b4a9086 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -30,6 +30,7 @@ #include "hw_md2.h" #include "../d_main.h" #include "../r_bsp.h" +#include "../r_fps.h" #include "../r_main.h" #include "../m_misc.h" #include "../w_wad.h" @@ -1360,8 +1361,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) { patch_t *gpatch, *blendgpatch; GLPatch_t *hwrPatch = NULL, *hwrBlendPatch = NULL; - INT32 durs = spr->mobj->state->tics; - INT32 tics = spr->mobj->tics; + float durs = (float)spr->mobj->state->tics; + float tics = (float)spr->mobj->tics; const boolean papersprite = (R_ThingIsPaperSprite(spr->mobj) && !R_ThingIsFloorSprite(spr->mobj)); const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(spr->mobj)); const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !R_ThingHorizontallyFlipped(spr->mobj)); @@ -1369,6 +1370,16 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) spriteframe_t *sprframe; INT32 mod; float finalscale; + interpmobjstate_t interp; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); + } fixed_t interpx = R_InterpolateFixed(spr->mobj->old_x, spr->mobj->x); fixed_t interpy = R_InterpolateFixed(spr->mobj->old_y, spr->mobj->y); @@ -1543,8 +1554,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (spr->mobj->frame & FF_ANIMATE) { // set duration and tics to be the correct values for FF_ANIMATE states - durs = spr->mobj->state->var2; - tics = spr->mobj->anim_duration; + durs = (float)spr->mobj->state->var2; + tics = (float)spr->mobj->anim_duration; } frame = (spr->mobj->frame & FF_FRAMEMASK); @@ -1568,6 +1579,9 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } #ifdef USE_MODEL_NEXTFRAME + // Interpolate the model interpolation. (lol) + tics -= FixedToFloat(rendertimefrac); + #define INTERPOLERATION_LIMIT TICRATE/4 if ( #ifdef BAD_MODEL_OPTIONS @@ -1620,13 +1634,13 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) #endif //Hurdler: it seems there is still a small problem with mobj angle - p.x = FIXED_TO_FLOAT(interpx); - p.y = FIXED_TO_FLOAT(interpy) + md2->offset; + p.x = FIXED_TO_FLOAT(interp.x); + p.y = FIXED_TO_FLOAT(interp.y) + md2->offset; if (flip) - p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); + p.z = FIXED_TO_FLOAT(interp.z + spr->mobj->height); else - p.z = FIXED_TO_FLOAT(interpz); + p.z = FIXED_TO_FLOAT(interp.z); if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) sprdef = &((skin_t *)spr->mobj->skin)->sprites[spr->mobj->sprite2]; @@ -1637,22 +1651,13 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (sprframe->rotate || papersprite) { - fixed_t anglef = INT32_MAX; - - if (spr->mobj->player) - { - anglef = AngleFixed(R_InterpolateAngle(spr->mobj->player->old_drawangle, spr->mobj->player->drawangle)); - } - else - { - anglef = AngleFixed(R_InterpolateAngle(spr->mobj->old_angle, spr->mobj->angle)); - } + fixed_t anglef = AngleFixed(interp.angle); p.angley = FIXED_TO_FLOAT(anglef); } else { - const fixed_t anglef = AngleFixed((R_PointToAngle(interpx, interpy))-ANGLE_180); + const fixed_t anglef = AngleFixed((R_PointToAngle(interp.x, interp.y))-ANGLE_180); p.angley = FIXED_TO_FLOAT(anglef); } diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 63ec8fa04..84ddc2492 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2702,7 +2702,7 @@ EXPORT void HWRAPI(CreateModelVBOs) (model_t *model) #define BUFFER_OFFSET(i) ((void*)(i)) -static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) { static GLRGBAFloat poly = {0,0,0,0}; static GLRGBAFloat tint = {0,0,0,0}; @@ -2732,11 +2732,11 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 scaley = vscale; scalez = hscale; - if (duration != 0 && duration != -1 && tics != -1) // don't interpolate if instantaneous or infinite in length + if (duration > 0.0 && tics >= 0.0) // don't interpolate if instantaneous or infinite in length { - UINT32 newtime = (duration - tics); // + 1; + float newtime = (duration - tics); // + 1; - pol = (newtime)/(float)duration; + pol = newtime / duration; if (pol > 1.0f) pol = 1.0f; @@ -2988,7 +2988,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 // -----------------+ // HWRAPI DrawModel : Draw a model // -----------------+ -EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) { DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, hscale, vscale, flipped, hflipped, Surface); } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 659c4edaa..71ce8b744 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -49,6 +49,7 @@ #endif #include "lua_hud.h" +#include "lua_hudlib_drawlist.h" #include "lua_hook.h" // SRB2Kart @@ -169,6 +170,8 @@ static INT32 cechoflags = 0; static tic_t resynch_ticker = 0; +static huddrawlist_h luahuddrawlist_scores; + //====================================================================== // HEADS UP INIT //====================================================================== @@ -304,6 +307,8 @@ void HU_Init(void) } HU_LoadGraphics(); + + luahuddrawlist_scores = LUA_HUD_CreateDrawList(); } patch_t *HU_CachePatch(const char *format, ...) @@ -369,12 +374,12 @@ static INT16 addy = 0; // use this to make the messages scroll smoothly when one static void HU_removeChatText_Mini(void) { - // MPC: Don't create new arrays, just iterate through an existing one + // MPC: Don't create new arrays, just iterate through an existing one size_t i; - for(i=0;i 0) chat_scrolltime--; } - else - { - chat_scrolltime = 0; - } - if (netgame) // would handle that in hu_drawminichat, but it's actually kinda awkward when you're typing a lot of messages. (only handle that in netgames duh) + if (netgame) { size_t i = 0; @@ -2082,6 +2083,8 @@ void HU_Drawer(void) else { typelines = 1; + chat_scrolltime = 0; + if (!OLDCHAT && cv_consolechat.value < 2 && netgame) // Don't display minimized chat if you set the mode to Window (Hidden) HU_drawMiniChat(); // draw messages in a cool fashion. } @@ -2104,7 +2107,12 @@ void HU_Drawer(void) { if (LUA_HudEnabled(hud_rankings)) HU_DrawRankings(); - LUAh_ScoresHUD(); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_scores); + LUAh_ScoresHUD(luahuddrawlist_scores); + } + LUA_HUD_DrawList(luahuddrawlist_scores); } if (demo.playback) diff --git a/src/i_system.h b/src/i_system.h index 47a6838a0..75e7b49e1 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -42,27 +42,32 @@ extern UINT8 keyboard_started; */ UINT32 I_GetFreeMem(UINT32 *total); -/** \brief Called by D_SRB2Loop, returns current time in tics. -*/ -tic_t I_GetTime(void); - -/** \brief Get the current time in tics including fractions. -*/ -float I_GetTimeFrac(void); - -/** \brief Returns precise time value for performance measurement. +/** \brief Returns precise time value for performance measurement. The precise + time should be a monotonically increasing counter, and will wrap. + precise_t is internally represented as an unsigned integer and + integer arithmetic may be used directly between values of precise_t. */ precise_t I_GetPreciseTime(void); -/** \brief Converts a precise_t to microseconds and casts it to a 32 bit integer. +/** \brief Get the precision of precise_t in units per second. Invocations of + this function for the program's duration MUST return the same value. */ -int I_PreciseToMicros(precise_t d); +UINT64 I_GetPrecisePrecision(void); -/** \brief The I_Sleep function +/** \brief Get the current time in rendering tics, including fractions. +*/ +double I_GetFrameTime(void); + +/** \brief Sleeps for the given duration in milliseconds. Depending on the + operating system's scheduler, the calling thread may give up its + time slice for a longer duration. The implementation should give a + best effort to sleep for the given duration, without spin-locking. + Calling code should check the current precise time after sleeping + and not assume the thread has slept for the expected duration. \return void */ -void I_Sleep(void); +void I_Sleep(UINT32 ms); boolean I_CheckFrameCap(precise_t start, precise_t end); diff --git a/src/i_time.c b/src/i_time.c new file mode 100644 index 000000000..c86973f6e --- /dev/null +++ b/src/i_time.c @@ -0,0 +1,117 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 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 i_time.c +/// \brief Timing for the system layer. + +#include "i_time.h" + +#include + +#include "command.h" +#include "doomtype.h" +#include "d_netcmd.h" +#include "m_fixed.h" +#include "i_system.h" + +timestate_t g_time; + +static CV_PossibleValue_t timescale_cons_t[] = {{FRACUNIT/20, "MIN"}, {20*FRACUNIT, "MAX"}, {0, NULL}}; +consvar_t cv_timescale = CVAR_INIT ("timescale", "1.0", CV_NETVAR|CV_CHEAT|CV_FLOAT, timescale_cons_t, NULL); + +static precise_t enterprecise, oldenterprecise; +static fixed_t entertic, oldentertics; +static double tictimer; + +tic_t I_GetTime(void) +{ + return g_time.time; +} + +void I_InitializeTime(void) +{ + g_time.time = 0; + g_time.timefrac = 0; + + enterprecise = 0; + oldenterprecise = 0; + tictimer = 0.0; + + CV_RegisterVar(&cv_timescale); + + // I_StartupTimer is preserved for potential subsystems that need to setup + // timing information for I_GetPreciseTime and sleeping + I_StartupTimer(); +} + +void I_UpdateTime(fixed_t timescale) +{ + double ticratescaled; + double elapsedseconds; + tic_t realtics; + + // get real tics + ticratescaled = (double)TICRATE * FIXED_TO_FLOAT(timescale); + + enterprecise = I_GetPreciseTime(); + elapsedseconds = (double)(enterprecise - oldenterprecise) / I_GetPrecisePrecision(); + tictimer += elapsedseconds; + while (tictimer > 1.0/ticratescaled) + { + entertic += 1; + tictimer -= 1.0/ticratescaled; + } + realtics = entertic - oldentertics; + oldentertics = entertic; + oldenterprecise = enterprecise; + + // Update global time state + g_time.time += realtics; + { + double fractional, integral; + fractional = modf(tictimer * ticratescaled, &integral); + g_time.timefrac = FLOAT_TO_FIXED(fractional); + } +} + +void I_SleepDuration(precise_t duration) +{ + UINT64 precision = I_GetPrecisePrecision(); + INT32 sleepvalue = cv_sleep.value; + UINT64 delaygranularity; + precise_t cur; + precise_t dest; + + { + double gran = round(((double)(precision / 1000) * sleepvalue * 2.1)); + delaygranularity = (UINT64)gran; + } + + cur = I_GetPreciseTime(); + dest = cur + duration; + + // the reason this is not dest > cur is because the precise counter may wrap + // two's complement arithmetic is our friend here, though! + // e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1 + // 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3 + while ((INT64)(dest - cur) > 0) + { + // If our cv_sleep value exceeds the remaining sleep duration, use the + // hard sleep function. + if (sleepvalue > 0 && (dest - cur) > delaygranularity) + { + I_Sleep(sleepvalue); + } + + // Otherwise, this is a spinloop. + + cur = I_GetPreciseTime(); + } +} diff --git a/src/i_time.h b/src/i_time.h new file mode 100644 index 000000000..cab36133b --- /dev/null +++ b/src/i_time.h @@ -0,0 +1,54 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 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 i_time.h +/// \brief Timing for the system layer. + +#ifndef __I_TIME_H__ +#define __I_TIME_H__ + +#include "command.h" +#include "doomtype.h" +#include "m_fixed.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct timestate_s { + tic_t time; + fixed_t timefrac; +} timestate_t; + +extern timestate_t g_time; +extern consvar_t cv_timescale; + +/** \brief Called by D_SRB2Loop, returns current time in game tics. +*/ +tic_t I_GetTime(void); + +/** \brief Initializes timing system. +*/ +void I_InitializeTime(void); + +void I_UpdateTime(fixed_t timescale); + +/** \brief Block for at minimum the duration specified. This function makes a + best effort not to oversleep, and will spinloop if sleeping would + take too long. However, callers should still check the current time + after this returns. +*/ +void I_SleepDuration(precise_t duration); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __I_TIME_H__ diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 04b37601f..761761399 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -36,7 +36,7 @@ #include "m_menu.h" // Player Setup menu color stuff #include "m_misc.h" // M_MapNumber #include "p_spec.h" // P_StartQuake -#include "i_system.h" // I_GetPreciseTime, I_PreciseToMicros +#include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "lua_script.h" #include "lua_libs.h" @@ -1444,7 +1444,7 @@ static int lib_pTeleportMove(lua_State *L) if (!thing) return LUA_ErrInvalid(L, "mobj_t"); LUA_Deprecated(L, "P_TeleportMove", "P_SetOrigin\" or \"P_MoveOrigin"); - lua_pushboolean(L, P_SetOrigin(thing, x, y, z)); + lua_pushboolean(L, P_MoveOrigin(thing, x, y, z)); LUA_PushUserdata(L, tmthing, META_MOBJ); P_SetTarget(&tmthing, ptmthing); return 2; @@ -1484,42 +1484,6 @@ static int lib_pMoveOrigin(lua_State *L) return 2; } -static int lib_pInitAngle(lua_State *L) -{ - mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - angle_t newValue = luaL_checkangle(L, 2); - NOHUD - INLEVEL - if (!thing) - return LUA_ErrInvalid(L, "mobj_t"); - P_InitAngle(thing, newValue); - return 0; -} - -static int lib_pInitPitch(lua_State *L) -{ - mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - angle_t newValue = luaL_checkangle(L, 2); - NOHUD - INLEVEL - if (!thing) - return LUA_ErrInvalid(L, "mobj_t"); - P_InitPitch(thing, newValue); - return 0; -} - -static int lib_pInitRoll(lua_State *L) -{ - mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - angle_t newValue = luaL_checkangle(L, 2); - NOHUD - INLEVEL - if (!thing) - return LUA_ErrInvalid(L, "mobj_t"); - P_InitRoll(thing, newValue); - return 0; -} - static int lib_pSlideMove(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -3844,7 +3808,7 @@ static int lib_kDeclareWeakspot(lua_State *L) static int lib_getTimeMicros(lua_State *L) { - lua_pushinteger(L, I_PreciseToMicros(I_GetPreciseTime())); + lua_pushinteger(L, I_GetPreciseTime() / (I_GetPrecisePrecision() / 1000000)); return 1; } @@ -3955,9 +3919,6 @@ static luaL_Reg lib[] = { {"P_TeleportMove",lib_pTeleportMove}, {"P_SetOrigin",lib_pSetOrigin}, {"P_MoveOrigin",lib_pMoveOrigin}, - {"P_InitAngle",lib_pInitAngle}, - {"P_InitPitch",lib_pInitPitch}, - {"P_InitRoll",lib_pInitRoll}, {"P_SlideMove",lib_pSlideMove}, {"P_BounceMove",lib_pBounceMove}, {"P_CheckSight", lib_pCheckSight}, diff --git a/src/lua_hook.h b/src/lua_hook.h index e44b60d75..7760c188b 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -12,6 +12,7 @@ #include "r_defs.h" #include "d_player.h" +#include "lua_hudlib_drawlist.h" enum hook { hook_NetVars=0, diff --git a/src/lua_hud.h b/src/lua_hud.h index 2d94c1b01..35db4ef87 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -10,6 +10,11 @@ /// \file lua_hud.h /// \brief HUD enable/disable flags for Lua scripting +#ifndef __LUA_HUD_H__ +#define __LUA_HUD_H__ + +#include "lua_hudlib_drawlist.h" + enum hud { hud_stagetitle = 0, hud_textspectator, @@ -42,8 +47,10 @@ extern boolean hud_running; boolean LUA_HudEnabled(enum hud option); -void LUAh_GameHUD(player_t *stplyr); -void LUAh_ScoresHUD(void); -void LUAh_TitleHUD(void); -void LUAh_TitleCardHUD(player_t *stplayr); -void LUAh_IntermissionHUD(void); +void LUAh_GameHUD(player_t *stplyr, huddrawlist_h list); +void LUAh_ScoresHUD(huddrawlist_h list); +void LUAh_TitleHUD(huddrawlist_h list); +void LUAh_TitleCardHUD(player_t *stplayr, huddrawlist_h list); +void LUAh_IntermissionHUD(huddrawlist_h list); + +#endif // __LUA_HUD_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 75a84ed18..b39a507ec 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -572,7 +572,8 @@ static int libd_draw(lua_State *L) { INT32 x, y, flags; patch_t *patch; - const UINT8 *colormap = NULL; + UINT8 *colormap = NULL; + huddrawlist_h list; HUDONLY x = luaL_checkinteger(L, 1); @@ -586,7 +587,14 @@ static int libd_draw(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawFixedPatch(x<height/2)< maxstrength) return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength); + lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + list = (huddrawlist_h) lua_touserdata(L, -1); + lua_pop(L, 1); + if (strength == maxstrength) // Allow as a shortcut for drawfill... { - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddDrawFill(list, 0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); + else + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); return 0; } - V_DrawFadeScreen(color, strength); + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddFadeScreen(list, color, strength); + else + V_DrawFadeScreen(color, strength); return 0; } static int libd_drawString(lua_State *L) { + huddrawlist_h list; fixed_t x = luaL_checkinteger(L, 1); fixed_t y = luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); @@ -910,6 +989,15 @@ static int libd_drawString(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY + + lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + list = (huddrawlist_h) lua_touserdata(L, -1); + lua_pop(L, 1); + + // okay, sorry, this is kind of ugly + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddDrawString(list, x, y, str, flags, align); + else switch(align) { // hu_font @@ -956,11 +1044,19 @@ static int libd_drawTitleCardString(lua_State *L) boolean rightalign = lua_optboolean(L, 5); INT32 timer = luaL_optinteger(L, 6, 0); INT32 threshold = luaL_optinteger(L, 7, 0); + huddrawlist_h list; flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY - V_DrawTitleCardString(x, y, str, flags, rightalign, timer, threshold); + lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + list = (huddrawlist_h) lua_touserdata(L, -1); + lua_pop(L, 1); + + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddDrawTitleCardString(list, x, y, flags, str, rightalign, timer, threshold); + else + V_DrawTitleCardString(x, y, str, flags, rightalign, timer, threshold); return 0; } @@ -970,11 +1066,19 @@ static int libd_drawKartString(lua_State *L) fixed_t y = luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE); + huddrawlist_h list; flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY - V_DrawKartString(x, y, flags, str); + lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + list = (huddrawlist_h) lua_touserdata(L, -1); + lua_pop(L, 1); + + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddDrawKartString(list, x, y, str, flags); + else + V_DrawKartString(x, y, flags, str); return 0; } @@ -1172,6 +1276,14 @@ static int libd_getusertransflag(lua_State *L) return 1; } +// Return the time elapsed for the previous frame, in tics. +static int libd_getDeltaTime(lua_State *L) +{ + HUDONLY + lua_pushfixed(L, renderdeltatics); + return 1; +} + static luaL_Reg lib_draw[] = { // cache {"patchExists", libd_patchExists}, @@ -1211,6 +1323,7 @@ static luaL_Reg lib_draw[] = { {"localTransFlag", libd_getlocaltransflag}, {"drawOnMinimap", libd_drawOnMinimap}, {"userTransFlag", libd_getusertransflag}, + {"getDeltaTime", libd_getDeltaTime}, {NULL, NULL} }; @@ -1358,11 +1471,14 @@ boolean LUA_HudEnabled(enum hud option) } // Hook for HUD rendering -void LUAh_GameHUD(player_t *stplayr) +void LUAh_GameHUD(player_t *stplayr, huddrawlist_h list) { if (!gL || !(hudAvailable & (1< + +#include "v_video.h" +#include "z_zone.h" + +enum drawitem_e { + DI_Draw = 0, + DI_DrawScaled, + DI_DrawStretched, + DI_DrawNum, + DI_DrawPaddedNum, + DI_DrawPingNum, + DI_DrawFill, + DI_DrawString, + DI_FadeScreen, + DI_DrawTitleCardString, + DI_DrawKartString, + DI_MAX, +}; + +// A single draw item with all possible arguments needed for a draw call. +typedef struct drawitem_s { + enum drawitem_e type; + fixed_t x; + fixed_t y; + fixed_t w; + fixed_t h; + INT32 c; + fixed_t scale; + fixed_t hscale; + fixed_t vscale; + patch_t *patch; + INT32 flags; + UINT16 basecolor; + UINT16 outlinecolor; + UINT8 *colormap; + UINT8 *basecolormap; + UINT8 *outlinecolormap; + fixed_t sx; + fixed_t sy; + INT32 num; + INT32 digits; + const char *str; + UINT16 color; + UINT8 strength; + INT32 align; + INT32 timer; + INT32 threshold; + boolean bossmode; +} drawitem_t; + +// The internal structure of a drawlist. +struct huddrawlist_s { + drawitem_t *items; + size_t items_capacity; + size_t items_len; + char *strbuf; + size_t strbuf_capacity; + size_t strbuf_len; +}; + +// alignment types for v.drawString +enum align { + align_left = 0, + align_center, + align_right, + align_small, + align_smallcenter, + align_smallright, + align_thin, + align_thincenter, + align_thinright +}; + +huddrawlist_h LUA_HUD_CreateDrawList(void) +{ + huddrawlist_h drawlist; + + drawlist = (huddrawlist_h) Z_CallocAlign(sizeof(struct huddrawlist_s), PU_STATIC, NULL, 64); + drawlist->items = NULL; + drawlist->items_capacity = 0; + drawlist->items_len = 0; + drawlist->strbuf = NULL; + drawlist->strbuf_capacity = 0; + drawlist->strbuf_len = 0; + + return drawlist; +} + +void LUA_HUD_ClearDrawList(huddrawlist_h list) +{ + // rather than deallocate, we'll just save the existing allocation and empty + // it out for reuse + + // this memset probably isn't necessary + if (list->items) + { + memset(list->items, 0, sizeof(drawitem_t) * list->items_capacity); + } + + list->items_len = 0; + + if (list->strbuf) + { + list->strbuf[0] = 0; + } + list->strbuf_len = 0; +} + +void LUA_HUD_DestroyDrawList(huddrawlist_h list) +{ + if (list == NULL) return; + + if (list->items) + { + Z_Free(list->items); + } + Z_Free(list); +} + +boolean LUA_HUD_IsDrawListValid(huddrawlist_h list) +{ + if (!list) return false; + + // that's all we can really do to check the validity of the handle right now + return true; +} + +static size_t AllocateDrawItem(huddrawlist_h list) +{ + if (!list) I_Error("can't allocate draw item: invalid list"); + if (list->items_capacity <= list->items_len + 1) + { + if (list->items_capacity == 0) list->items_capacity = 128; + else list->items_capacity *= 2; + list->items = (drawitem_t *) Z_ReallocAlign(list->items, sizeof(struct drawitem_s) * list->items_capacity, PU_STATIC, NULL, 64); + } + + return list->items_len++; +} + +// copy string to list's internal string buffer +// lua can deallocate the string before we get to use it, so it's important to +// keep our own copy +static const char *CopyString(huddrawlist_h list, const char* str) +{ + size_t lenstr; + + if (!list) I_Error("can't allocate string; invalid list"); + lenstr = strlen(str); + if (list->strbuf_capacity <= list->strbuf_len + lenstr + 1) + { + if (list->strbuf_capacity == 0) list->strbuf_capacity = 256; + else list->strbuf_capacity *= 2; + list->strbuf = (char*) Z_ReallocAlign(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL, 8); + } + const char *result = (const char *) &list->strbuf[list->strbuf_len]; + strncpy(&list->strbuf[list->strbuf_len], str, lenstr + 1); + list->strbuf_len += lenstr + 1; + return result; +} + +void LUA_HUD_AddDraw( + huddrawlist_h list, + INT32 x, + INT32 y, + patch_t *patch, + INT32 flags, + UINT8 *colormap +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_Draw; + item->x = x; + item->y = y; + item->patch = patch; + item->flags = flags; + item->colormap = colormap; +} + +void LUA_HUD_AddDrawScaled( + huddrawlist_h list, + fixed_t x, + fixed_t y, + fixed_t scale, + patch_t *patch, + INT32 flags, + UINT8 *colormap +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawScaled; + item->x = x; + item->y = y; + item->scale = scale; + item->patch = patch; + item->flags = flags; + item->colormap = colormap; +} + +void LUA_HUD_AddDrawStretched( + huddrawlist_h list, + fixed_t x, + fixed_t y, + fixed_t hscale, + fixed_t vscale, + patch_t *patch, + INT32 flags, + UINT8 *colormap +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawStretched; + item->x = x; + item->y = y; + item->hscale = hscale; + item->vscale = vscale; + item->patch = patch; + item->flags = flags; + item->colormap = colormap; +} + +void LUA_HUD_AddDrawNum( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 num, + INT32 flags +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawNum; + item->x = x; + item->y = y; + item->num = num; + item->flags = flags; +} + +void LUA_HUD_AddDrawPaddedNum( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 num, + INT32 digits, + INT32 flags +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawPaddedNum; + item->x = x; + item->y = y; + item->num = num; + item->digits = digits; + item->flags = flags; +} + +void LUA_HUD_AddDrawPingNum( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 flags, + INT32 num, + const UINT8 *colormap +) +{ + *(int*)(NULL + 1); +} + +void LUA_HUD_AddDrawFill( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 w, + INT32 h, + INT32 c +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawFill; + item->x = x; + item->y = y; + item->w = w; + item->h = h; + item->c = c; +} + +void LUA_HUD_AddDrawString( + huddrawlist_h list, + fixed_t x, + fixed_t y, + const char *str, + INT32 flags, + INT32 align +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawString; + item->x = x; + item->y = y; + item->str = CopyString(list, str); + item->flags = flags; + item->align = align; +} + +void LUA_HUD_AddFadeScreen( + huddrawlist_h list, + UINT16 color, + UINT8 strength +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_FadeScreen; + item->color = color; + item->strength = strength; +} + +void LUA_HUD_AddDrawTitleCardString( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 flags, + const char *str, + boolean bossmode, + INT32 timer, + INT32 threshold +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawTitleCardString; + item->x = x; + item->y = y; + item->flags = flags; + item->str = CopyString(list, str); + item->bossmode = bossmode; + item->timer = timer; + item->threshold = threshold; +} + +void LUA_HUD_AddDrawKartString( + huddrawlist_h list, + fixed_t x, + fixed_t y, + INT32 flags, + const char *str +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawKartString; + item->x = x; + item->y = y; + item->flags = flags; + item->str = CopyString(list, str); +} + +void LUA_HUD_DrawList(huddrawlist_h list) +{ + size_t i; + + if (!list) I_Error("HUD drawlist invalid"); + if (list->items_len <= 0) return; + if (!list->items) I_Error("HUD drawlist->items invalid"); + + for (i = 0; i < list->items_len; i++) + { + drawitem_t *item = &list->items[i]; + + switch (item->type) + { + case DI_Draw: + V_DrawFixedPatch(item->x<y<flags, item->patch, item->colormap); + break; + case DI_DrawScaled: + V_DrawFixedPatch(item->x, item->y, item->scale, item->flags, item->patch, item->colormap); + break; + case DI_DrawStretched: + V_DrawStretchyFixedPatch(item->x, item->y, item->hscale, item->vscale, item->flags, item->patch, item->colormap); + break; + case DI_DrawNum: + V_DrawTallNum(item->x, item->y, item->flags, item->num); + break; + case DI_DrawPaddedNum: + V_DrawPaddedTallNum(item->x, item->y, item->flags, item->num, item->digits); + break; + case DI_DrawPingNum: + V_DrawPingNum(item->x, item->y, item->flags, item->num, item->colormap); + break; + case DI_DrawFill: + V_DrawFill(item->x, item->y, item->w, item->h, item->c); + break; + case DI_DrawString: + switch(item->align) + { + // hu_font + case align_left: + V_DrawString(item->x, item->y, item->flags, item->str); + break; + case align_center: + V_DrawCenteredString(item->x, item->y, item->flags, item->str); + break; + case align_right: + V_DrawRightAlignedString(item->x, item->y, item->flags, item->str); + break; + // hu_font, 0.5x scale + case align_small: + V_DrawSmallString(item->x, item->y, item->flags, item->str); + break; + case align_smallcenter: + V_DrawCenteredSmallString(item->x, item->y, item->flags, item->str); + break; + case align_smallright: + V_DrawRightAlignedSmallString(item->x, item->y, item->flags, item->str); + break; + // tny_font + case align_thin: + V_DrawThinString(item->x, item->y, item->flags, item->str); + break; + case align_thincenter: + V_DrawCenteredThinString(item->x, item->y, item->flags, item->str); + break; + case align_thinright: + V_DrawRightAlignedThinString(item->x, item->y, item->flags, item->str); + break; + } + break; + case DI_FadeScreen: + V_DrawFadeScreen(item->color, item->strength); + break; + case DI_DrawTitleCardString: + V_DrawTitleCardString(item->x, item->y, item->str, item->flags, item->bossmode, item->timer, item->threshold); + break; + case DI_DrawKartString: + V_DrawKartString(item->x, item->y, item->flags, item->str); + break; + default: + I_Error("can't draw draw list item: invalid draw list item type"); + continue; + } + } +} diff --git a/src/lua_hudlib_drawlist.h b/src/lua_hudlib_drawlist.h new file mode 100644 index 000000000..44a332460 --- /dev/null +++ b/src/lua_hudlib_drawlist.h @@ -0,0 +1,133 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2022-2022 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 lua_hudlib_drawlist.h +/// \brief a data structure for managing cached drawlists for the Lua hud lib + +// The idea behinds this module is to cache drawcall information into an ordered +// list to repeat the same draw operations in later frames. It's used to ensure +// that the HUD hooks from Lua are called at precisely 35hz to avoid problems +// with variable framerates in existing Lua addons. + +#ifndef __LUA_HUDLIB_DRAWLIST__ +#define __LUA_HUDLIB_DRAWLIST__ + +#include "doomtype.h" +#include "r_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct huddrawlist_s *huddrawlist_h; + +// Create a new drawlist. Returns a handle to it. +huddrawlist_h LUA_HUD_CreateDrawList(void); +// Clears the draw list. +void LUA_HUD_ClearDrawList(huddrawlist_h list); +// Destroys the drawlist, invalidating the given handle +void LUA_HUD_DestroyDrawList(huddrawlist_h list); +boolean LUA_HUD_IsDrawListValid(huddrawlist_h list); + +void LUA_HUD_AddDraw( + huddrawlist_h list, + INT32 x, + INT32 y, + patch_t *patch, + INT32 flags, + UINT8 *colormap +); +void LUA_HUD_AddDrawScaled( + huddrawlist_h list, + fixed_t x, + fixed_t y, + fixed_t scale, + patch_t *patch, + INT32 flags, + UINT8 *colormap +); +void LUA_HUD_AddDrawStretched( + huddrawlist_h list, + fixed_t x, + fixed_t y, + fixed_t hscale, + fixed_t vscale, + patch_t *patch, + INT32 flags, + UINT8 *colormap +); +void LUA_HUD_AddDrawNum( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 num, + INT32 flags +); +void LUA_HUD_AddDrawPaddedNum( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 num, + INT32 digits, + INT32 flags +); +void LUA_HUD_AddDrawPingNum( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 flags, + INT32 num, + const UINT8 *colormap +); +void LUA_HUD_AddDrawFill( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 w, + INT32 h, + INT32 c +); +void LUA_HUD_AddDrawString( + huddrawlist_h list, + fixed_t x, + fixed_t y, + const char *str, + INT32 flags, + INT32 align +); +void LUA_HUD_AddFadeScreen( + huddrawlist_h list, + UINT16 color, + UINT8 strength +); +void LUA_HUD_AddDrawTitleCardString( + huddrawlist_h list, + INT32 x, + INT32 y, + INT32 flags, + const char *str, + boolean bossmode, + INT32 timer, + INT32 threshold +); +void LUA_HUD_AddDrawKartString( + huddrawlist_h list, + fixed_t x, + fixed_t y, + INT32 flags, + const char *str +); + +// Draws the given draw list +void LUA_HUD_DrawList(huddrawlist_h list); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __LUA_HUDLIB_DRAWLIST__ diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 4c2a4f24d..4fd02d9a1 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -778,6 +778,7 @@ static int mobj_set(lua_State *L) scale = FRACUNIT/100; mo->destscale = scale; P_SetScale(mo, scale); + mo->old_scale = scale; break; } case mobj_destscale: diff --git a/src/m_anigif.c b/src/m_anigif.c index 41f99254e..a6c830fa3 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -608,7 +608,7 @@ static void GIF_framewrite(void) { // golden's attempt at creating a "dynamic delay" UINT16 mingifdelay = 10; // minimum gif delay in milliseconds (keep at 10 because gifs can't get more precise). - gif_delayus += I_PreciseToMicros(I_GetPreciseTime() - gif_prevframetime); // increase delay by how much time was spent between last measurement + gif_delayus += (I_GetPreciseTime() - gif_prevframetime) / (I_GetPrecisePrecision() / 1000000); // increase delay by how much time was spent between last measurement if (gif_delayus/1000 >= mingifdelay) // delay is big enough to be able to effect gif frame delay? { @@ -621,7 +621,7 @@ static void GIF_framewrite(void) { float delayf = ceil(100.0f/NEWTICRATE); - delay = (UINT16)I_PreciseToMicros((I_GetPreciseTime() - gif_prevframetime))/10/1000; + delay = (UINT16)((I_GetPreciseTime() - gif_prevframetime)) / (I_GetPrecisePrecision() / 1000000) /10/1000; if (delay < (UINT16)(delayf)) delay = (UINT16)(delayf); diff --git a/src/m_menu.c b/src/m_menu.c index 1c5191127..9359af93e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -22,6 +22,7 @@ #include "d_main.h" #include "d_netcmd.h" #include "console.h" +#include "r_fps.h" #include "r_local.h" #include "hu_stuff.h" #include "g_game.h" @@ -31,6 +32,7 @@ // Data. #include "sounds.h" #include "s_sound.h" +#include "i_time.h" #include "i_system.h" #include "i_threads.h" @@ -6958,6 +6960,7 @@ static void M_DrawLoad(void) INT32 ymod = 0, offset = 0; M_DrawMenuTitle(); + fixed_t scrollfrac = FixedDiv(2, 3); if (menumovedir != 0) //movement illusion { @@ -9340,7 +9343,7 @@ static void M_HandleConnectIP(INT32 choice) // Tails 03-02-2002 // used for skin display on player setup menu -static INT32 multi_tics; +static fixed_t multi_tics; static state_t *multi_state; static UINT8 multi_spr2; @@ -9570,7 +9573,8 @@ static void M_DrawSetupMultiPlayerMenu(void) #undef iconwidth // anim the player in the box - if (--multi_tics <= 0) + multi_tics -= renderdeltatics; + if (multi_tics <= 0) { st = multi_state->nextstate; if (st != S_NULL) @@ -9860,7 +9864,7 @@ static void M_SetupMultiPlayer(INT32 choice) (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; - multi_tics = multi_state->tics; + multi_tics = multi_state->tics * FRACUNIT; strcpy(setupm_name, cv_playername[0].string); @@ -9904,7 +9908,7 @@ static void M_SetupMultiPlayer2(INT32 choice) (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; - multi_tics = multi_state->tics; + multi_tics = multi_state->tics * FRACUNIT; strcpy (setupm_name, cv_playername[1].string); // set for splitscreen secondary player @@ -11426,7 +11430,8 @@ void M_QuitResponse(INT32 ch) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); V_DrawSmallScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001 I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 - I_Sleep(); + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); } } I_Quit(); diff --git a/src/m_misc.c b/src/m_misc.c index cf5053078..4078e15c4 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -36,6 +36,7 @@ #include "v_video.h" #include "z_zone.h" #include "g_input.h" +#include "i_time.h" #include "i_video.h" #include "d_main.h" #include "m_argv.h" diff --git a/src/m_perfstats.c b/src/m_perfstats.c index b58599b6d..d1a211367 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -15,6 +15,7 @@ #include "d_netcmd.h" #include "r_main.h" #include "i_system.h" +#include "i_time.h" #include "z_zone.h" #include "p_local.h" @@ -116,7 +117,7 @@ static void M_DrawPerfString(perfstatcol_t *col, int type) for (row = col->rows; row->lores_label; ++row) { if (type == PERF_TIME) - value = I_PreciseToMicros(*(precise_t *)row->value); + value = (*(precise_t *)row->value) / (I_GetPrecisePrecision() / 1000000); else value = *(int *)row->value; @@ -565,7 +566,7 @@ void M_DrawPerfStats(void) len = (int)strlen(str); if (len > 20) str += len - 20; - snprintf(s, sizeof s - 1, "%20s: %d", str, I_PreciseToMicros(thinkframe_hooks[i].time_taken)); + snprintf(s, sizeof s - 1, "%20s: %lld", str, (thinkframe_hooks[i].time_taken) / (I_GetPrecisePrecision() / 1000000)); V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | text_color, s); y += 4; // repeated code! if (y > 192) diff --git a/src/p_ceilng.c b/src/p_ceilng.c index e20e610fe..062ebbda6 100644 --- a/src/p_ceilng.c +++ b/src/p_ceilng.c @@ -13,6 +13,7 @@ #include "doomdef.h" #include "p_local.h" +#include "r_fps.h" #include "r_main.h" #include "s_sound.h" #include "z_zone.h" @@ -602,6 +603,9 @@ INT32 EV_DoCeiling(line_t *line, ceiling_e type) ceiling->tag = tag; ceiling->type = type; firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } @@ -679,6 +683,10 @@ INT32 EV_DoCrush(line_t *line, ceiling_e type) ceiling->tag = tag; ceiling->type = type; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } diff --git a/src/p_floor.c b/src/p_floor.c index a6855067a..76fd476af 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -16,6 +16,7 @@ #include "m_random.h" #include "p_local.h" #include "p_slopes.h" +#include "r_fps.h" #include "r_state.h" #include "s_sound.h" #include "z_zone.h" @@ -573,6 +574,8 @@ void T_ContinuousFalling(continuousfall_t *faller) { faller->sector->ceilingheight = faller->ceilingstartheight; faller->sector->floorheight = faller->floorstartheight; + + R_ClearLevelInterpolatorState(&faller->thinker); } P_CheckSector(faller->sector, false); // you might think this is irrelevant. you would be wrong @@ -1948,6 +1951,9 @@ void EV_DoFloor(line_t *line, floor_e floortype) } firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&dofloor->thinker, sec, false); } } @@ -2078,6 +2084,10 @@ void EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed) default: break; } + + // interpolation + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, true); } } @@ -2232,6 +2242,10 @@ void EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline) bouncer->speed = momz/2; bouncer->distance = FRACUNIT; bouncer->low = true; + + // interpolation + R_CreateInterpolator_SectorPlane(&bouncer->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&bouncer->thinker, sec, true); } // For T_ContinuousFalling special @@ -2257,6 +2271,10 @@ void EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boole faller->destheight = backwards ? backsector->ceilingheight : backsector->floorheight; faller->direction = backwards ? 1 : -1; + + // interpolation + R_CreateInterpolator_SectorPlane(&faller->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&faller->thinker, sec, true); } // Some other 3dfloor special things Tails 03-11-2002 (Search p_mobj.c for description) @@ -2309,6 +2327,10 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating, crumble->sector->crumblestate = CRUMBLE_ACTIVATED; + // interpolation + R_CreateInterpolator_SectorPlane(&crumble->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&crumble->thinker, sec, true); + TAG_ITER_SECTORS(tag, i) { foundsec = §ors[i]; @@ -2360,6 +2382,10 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) block->ceilingstartheight = block->sector->ceilingheight; block->tag = (INT16)Tag_FGet(§or->tags); + // interpolation + R_CreateInterpolator_SectorPlane(&block->thinker, roversec, false); + R_CreateInterpolator_SectorPlane(&block->thinker, roversec, true); + if (itsamonitor) { oldx = thing->x; @@ -2368,9 +2394,9 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) } P_UnsetThingPosition(thing); - thing->x = sector->soundorg.x; - thing->y = sector->soundorg.y; - thing->z = topheight; + thing->x = thing->old_x = sector->soundorg.x; + thing->y = thing->old_y = sector->soundorg.y; + thing->z = thing->old_z = topheight; thing->momz = FixedMul(6*FRACUNIT, thing->scale); P_SetThingPosition(thing); if (thing->flags & MF_SHOOTABLE) @@ -2391,9 +2417,9 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) if (itsamonitor && thing) { P_UnsetThingPosition(thing); - thing->x = oldx; - thing->y = oldy; - thing->z = oldz; + thing->x = thing->old_x = oldx; + thing->y = thing->old_y = oldy; + thing->z = thing->old_z = oldz; thing->momx = 1; thing->momy = 1; P_SetThingPosition(thing); diff --git a/src/p_map.c b/src/p_map.c index 6954d6e11..708cb182f 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -19,6 +19,7 @@ #include "m_random.h" #include "p_local.h" #include "p_setup.h" // NiGHTS stuff +#include "r_fps.h" #include "r_state.h" #include "r_main.h" #include "r_sky.h" @@ -181,6 +182,7 @@ void P_InitRoll(mobj_t *thing, angle_t newValue) thing->roll = thing->old_roll = newValue; } + // ========================================================================= // MOVEMENT ITERATOR FUNCTIONS // ========================================================================= @@ -654,9 +656,9 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // underneath if (tmthing->eflags & MFE_VERTICALFLIP) - thing->z = tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale); + P_SetOrigin(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale)); else - thing->z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale); + P_SetOrigin(thing, thing->x, thing->y, tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale)); if (thing->flags & MF_SHOOTABLE) P_DamageMobj(thing, tmthing, tmthing, 1, 0); return true; diff --git a/src/p_mobj.c b/src/p_mobj.c index 3a4cd1751..7fb3be772 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -18,6 +18,7 @@ #include "hu_stuff.h" #include "p_local.h" #include "p_setup.h" +#include "r_fps.h" #include "r_main.h" #include "r_skins.h" #include "r_sky.h" @@ -3818,13 +3819,7 @@ void P_NullPrecipThinker(precipmobj_t *mobj) void P_PrecipThinker(precipmobj_t *mobj) { - // reset old state (for interpolation) - mobj->old_x = mobj->x; - mobj->old_y = mobj->y; - mobj->old_z = mobj->z; - mobj->old_angle = mobj->angle; - mobj->old_pitch = mobj->pitch; - mobj->old_roll = mobj->roll; + R_ResetPrecipitationMobjInterpolationState(mobj); P_CycleStateAnimation((mobj_t *)mobj); @@ -3872,12 +3867,14 @@ void P_PrecipThinker(precipmobj_t *mobj) if ((mobj->info->deathstate == S_NULL) || (mobj->precipflags & PCF_PIT)) // no splashes on sky or bottomless pits { mobj->z = mobj->ceilingz; + R_ResetPrecipitationMobjInterpolationState(mobj); } else { P_SetPrecipMobjState(mobj, mobj->info->deathstate); mobj->z = mobj->floorz; mobj->precipflags |= PCF_SPLASH; + R_ResetPrecipitationMobjInterpolationState(mobj); } } } @@ -5431,6 +5428,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->z = mobj->target->z + mobj->target->height + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale); else mobj->z = mobj->target->z - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale) - mobj->height; + + mobj->old_z = mobj->z; + break; case MT_LOCKONINF: if (!(mobj->flags2 & MF2_STRONGBOX)) @@ -5442,6 +5442,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->z = mobj->threshold + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale); else mobj->z = mobj->threshold - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale); + + mobj->old_z = mobj->z; + break; case MT_FLAMEJET: P_FlameJetSceneryThink(mobj); @@ -10322,13 +10325,7 @@ 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 initial old positions (for interpolation) - mobj->old_x = mobj->x; - mobj->old_y = mobj->y; - mobj->old_z = mobj->z; - mobj->old_angle = mobj->angle; - mobj->old_pitch = mobj->pitch; - mobj->old_roll = mobj->roll; + R_AddMobjInterpolator(mobj); return mobj; } @@ -10381,13 +10378,7 @@ 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; - mobj->old_angle = mobj->angle; - mobj->old_pitch = mobj->pitch; - mobj->old_roll = mobj->roll; + R_ResetPrecipitationMobjInterpolationState(mobj); return mobj; } @@ -10519,6 +10510,8 @@ void P_RemoveMobj(mobj_t *mobj) memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t)); #endif + R_RemoveMobjInterpolator(mobj); + // free block if (!mobj->thinker.next) { // Uh-oh, the mobj doesn't think, P_RemoveThinker would never go through! @@ -13479,18 +13472,43 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo newmobj->eflags |= MFE_VERTICALFLIP; newmobj->flags2 |= MF2_OBJECTFLIP; newmobj->z = mobj->z + mobj->height - zofs - newmobj->height; + + newmobj->old_z = mobj->old_z + mobj->height - zofs - newmobj->height; + newmobj->old_z2 = mobj->old_z2 + mobj->height - zofs - newmobj->height; + } + else + { + newmobj->old_z = mobj->old_z + zofs; + newmobj->old_z2 = mobj->old_z2 + zofs; } - // EXPERIMENT: Let all objects set their interp values relative to their owner's old values. - // This will hopefully create a lot less mobj-specific spawn cases, - // but if there's any weird scenarios feel free to remove again. + newmobj->destscale = mobj->destscale; + P_SetScale(newmobj, mobj->scale); + + newmobj->old_x2 = mobj->old_x2 + xofs; + newmobj->old_y2 = mobj->old_y2 + yofs; newmobj->old_x = mobj->old_x + xofs; newmobj->old_y = mobj->old_y + yofs; - newmobj->old_z = mobj->old_z + zofs; - /* - newmobj->angle = mobj->angle; - newmobj->old_angle = mobj->old_angle; - */ + + // This angle hack is needed for Lua scripts that set the angle after + // spawning, to avoid erroneous interpolation. + if (mobj->player) + { + newmobj->old_angle2 = mobj->player->old_drawangle2; + newmobj->old_angle = mobj->player->old_drawangle; + } + else + { + newmobj->old_angle2 = mobj->old_angle2; + newmobj->old_angle = mobj->old_angle; + } + + newmobj->old_scale2 = mobj->old_scale2; + newmobj->old_scale = mobj->old_scale; + newmobj->old_spritexscale = mobj->old_spritexscale; + newmobj->old_spriteyscale = mobj->old_spriteyscale; + newmobj->old_spritexoffset = mobj->old_spritexoffset; + newmobj->old_spriteyoffset = mobj->old_spriteyoffset; return newmobj; } diff --git a/src/p_mobj.h b/src/p_mobj.h index 2dfeff0a4..09f1c9a87 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -282,6 +282,7 @@ typedef struct mobj_s // Info for drawing: position. fixed_t x, y, z; fixed_t old_x, old_y, old_z; // position interpolation + fixed_t old_x2, old_y2, old_z2; // More list: links in sector (if needed) struct mobj_s *snext; @@ -290,6 +291,7 @@ typedef struct mobj_s // More drawing info: to determine current sprite. angle_t angle, pitch, roll; // orientation angle_t old_angle, old_pitch, old_roll; // orientation interpolation + angle_t old_angle2, old_pitch2, old_roll2; angle_t rollangle; spritenum_t sprite; // used to find patch_t and flip value UINT32 frame; // frame number, plus bits see p_pspr.h @@ -299,6 +301,8 @@ typedef struct mobj_s UINT32 renderflags; // render flags fixed_t spritexscale, spriteyscale; fixed_t spritexoffset, spriteyoffset; + fixed_t old_spritexscale, old_spriteyscale; + fixed_t old_spritexoffset, old_spriteyoffset; struct pslope_s *floorspriteslope; // The slope that the floorsprite is rotated by struct msecnode_s *touching_sectorlist; // a linked list of sectors where this object appears @@ -377,6 +381,8 @@ typedef struct mobj_s UINT32 mobjnum; // A unique number for this mobj. Used for restoring pointers on save games. fixed_t scale; + fixed_t old_scale; // interpolation + fixed_t old_scale2; fixed_t destscale; fixed_t scalespeed; @@ -391,6 +397,7 @@ typedef struct mobj_s struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?) + boolean resetinterp; // if true, some fields should not be interpolated (see R_InterpolateMobjState implementation) boolean colorized; // Whether the mobj uses the rainbow colormap boolean mirrored; // The object's rotations will be mirrored left to right, e.g., see frame AL from the right and AR from the left @@ -423,6 +430,7 @@ typedef struct precipmobj_s // Info for drawing: position. fixed_t x, y, z; fixed_t old_x, old_y, old_z; // position interpolation + fixed_t old_x2, old_y2, old_z2; // More list: links in sector (if needed) struct precipmobj_s *snext; @@ -431,6 +439,7 @@ typedef struct precipmobj_s // More drawing info: to determine current sprite. angle_t angle, pitch, roll; // orientation angle_t old_angle, old_pitch, old_roll; // orientation interpolation + angle_t old_angle2, old_pitch2, old_roll2; angle_t rollangle; spritenum_t sprite; // used to find patch_t and flip value UINT32 frame; // frame number, plus bits see p_pspr.h @@ -440,6 +449,8 @@ typedef struct precipmobj_s UINT32 renderflags; // render flags fixed_t spritexscale, spriteyscale; fixed_t spritexoffset, spriteyoffset; + fixed_t old_spritexscale, old_spriteyscale; + fixed_t old_spritexoffset, old_spriteyoffset; struct pslope_s *floorspriteslope; // The slope that the floorsprite is rotated by struct mprecipsecnode_s *touching_sectorlist; // a linked list of sectors where this object appears diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 594648546..590ca66b1 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -24,6 +24,7 @@ #include "p_tick.h" #include "p_local.h" #include "p_polyobj.h" +#include "r_fps.h" #include "r_main.h" #include "r_state.h" #include "r_defs.h" @@ -2051,6 +2052,9 @@ boolean EV_DoPolyObjRotate(polyrotdata_t *prdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + th->turnobjs = prdata->turnobjs; // apply action to mirroring polyobjects as well @@ -2112,6 +2116,9 @@ boolean EV_DoPolyObjMove(polymovedata_t *pmdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // apply action to mirroring polyobjects as well start = 0; while ((po = Polyobj_GetChild(oldpo, &start))) @@ -2127,8 +2134,10 @@ boolean EV_DoPolyObjMove(polymovedata_t *pmdata) boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) { polyobj_t *po; + polyobj_t *oldpo; polywaypoint_t *th; mobj_t *first = NULL; + INT32 start; if (!(po = Polyobj_GetForNum(pwdata->polyObjNum))) { @@ -2179,6 +2188,26 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) th->continuous = false; } + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // T_PolyObjWaypoint is the only polyobject movement + // that can adjust z, so we add these ones too. + R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, false); + R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, true); + + // Most other polyobject functions handle children by recursively + // giving each child another thinker. T_PolyObjWaypoint handles + // it manually though, which means we need to manually give them + // interpolation here instead. + start = 0; + oldpo = po; + while ((po = Polyobj_GetChild(oldpo, &start))) + { + R_CreateInterpolator_Polyobj(&th->thinker, po); + R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, false); + R_CreateInterpolator_SectorPlane(&th->thinker, po->lines[0]->backsector, true); + } + th->pointnum = first->health; return true; @@ -2227,6 +2256,9 @@ static void Polyobj_doSlideDoor(polyobj_t *po, polydoordata_t *doordata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // start action on mirroring polyobjects as well start = 0; while ((po = Polyobj_GetChild(oldpo, &start))) @@ -2267,6 +2299,9 @@ static void Polyobj_doSwingDoor(polyobj_t *po, polydoordata_t *doordata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // start action on mirroring polyobjects as well start = 0; while ((po = Polyobj_GetChild(oldpo, &start))) @@ -2338,6 +2373,9 @@ boolean EV_DoPolyObjDisplace(polydisplacedata_t *prdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // apply action to mirroring polyobjects as well start = 0; while ((po = Polyobj_GetChild(oldpo, &start))) @@ -2384,6 +2422,9 @@ boolean EV_DoPolyObjRotDisplace(polyrotdisplacedata_t *prdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // apply action to mirroring polyobjects as well start = 0; while ((po = Polyobj_GetChild(oldpo, &start))) @@ -2488,6 +2529,9 @@ boolean EV_DoPolyObjFlag(polyflagdata_t *pfdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + // apply action to mirroring polyobjects as well start = 0; while ((po = Polyobj_GetChild(oldpo, &start))) diff --git a/src/p_saveg.c b/src/p_saveg.c index 18cef0e94..5a4aba435 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -22,6 +22,7 @@ #include "p_setup.h" #include "p_saveg.h" #include "r_data.h" +#include "r_fps.h" #include "r_textures.h" #include "r_things.h" #include "r_skins.h" @@ -3156,6 +3157,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) mobj->info = (mobjinfo_t *)next; // temporarily, set when leave this function + R_AddMobjInterpolator(mobj); + return &mobj->thinker; } diff --git a/src/p_setup.c b/src/p_setup.c index 9894523a5..793ebeae0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -21,6 +21,7 @@ #include "p_spec.h" #include "p_saveg.h" +#include "i_time.h" #include "i_video.h" // for I_FinishUpdate().. #include "r_sky.h" #include "i_system.h" @@ -32,6 +33,7 @@ #include "r_picformats.h" #include "r_sky.h" #include "r_draw.h" +#include "r_fps.h" // R_ResetViewInterpolation in level load #include "s_sound.h" #include "st_stuff.h" @@ -4159,7 +4161,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) while (nowtime < endtime) \ { \ while (!((nowtime = I_GetTime()) - lastwipetic)) \ - I_Sleep(); \ + { \ + I_Sleep(cv_sleep.value); \ + I_UpdateTime(cv_timescale.value); \ + } \ lastwipetic = nowtime; \ if (moviemode) \ M_SaveFrame(); \ @@ -4296,7 +4301,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) Patch_FreeTag(PU_PATCH_ROTATED); Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); + R_InitializeLevelInterpolators(); + P_InitThinkers(); + R_InitMobjInterpolators(); P_InitCachedActions(); if (!fromnetsave && savedata.lives > 0) diff --git a/src/p_slopes.c b/src/p_slopes.c index b35316e6d..f8846f531 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -22,6 +22,7 @@ #include "r_main.h" #include "p_maputl.h" #include "w_wad.h" +#include "r_fps.h" #include "k_kart.h" // K_PlayerEBrake pslope_t *slopelist = NULL; @@ -241,6 +242,9 @@ static inline void P_AddDynSlopeThinker (pslope_t* slope, dynplanetype_t type, l th->type = type; P_AddThinker(THINK_DYNSLOPE, &th->thinker); + + // interpolation + R_CreateInterpolator_DynSlope(&th->thinker, slope); } diff --git a/src/p_spec.c b/src/p_spec.c index da318a8eb..970c74401 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "p_setup.h" // levelflats for flat animation #include "r_data.h" +#include "r_fps.h" #include "r_textures.h" #include "m_random.h" #include "p_mobj.h" @@ -5512,6 +5513,10 @@ static void P_AddFloatThinker(sector_t *sec, UINT16 tag, line_t *sourceline) floater->sector = sec; floater->tag = (INT16)tag; floater->sourceline = sourceline; + + // interpolation + R_CreateInterpolator_SectorPlane(&floater->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&floater->thinker, sec, true); } /** @@ -5541,6 +5546,9 @@ static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, displace->speed = speed; displace->type = type; displace->reverse = reverse; + + // interpolation + R_CreateInterpolator_SectorPlane(&displace->thinker, §ors[affectee], false); } /** Adds a Mario block thinker, which changes the block's texture between blank @@ -5600,6 +5608,10 @@ static void P_AddRaiseThinker(sector_t *sec, INT16 tag, fixed_t speed, fixed_t c raise->flags |= RF_REVERSE; if (spindash) raise->flags |= RF_SPINDASH; + + // interpolation + R_CreateInterpolator_SectorPlane(&raise->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&raise->thinker, sec, true); } static void P_AddAirbob(sector_t *sec, INT16 tag, fixed_t dist, boolean raise, boolean spindash, boolean dynamic) @@ -5625,6 +5637,10 @@ static void P_AddAirbob(sector_t *sec, INT16 tag, fixed_t dist, boolean raise, b airbob->flags |= RF_SPINDASH; if (dynamic) airbob->flags |= RF_DYNAMIC; + + // interpolation + R_CreateInterpolator_SectorPlane(&airbob->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&airbob->thinker, sec, true); } /** Adds a thwomp thinker. @@ -5665,6 +5681,10 @@ static inline void P_AddThwompThinker(sector_t *sec, line_t *sourceline, fixed_t sec->ceilingdata = thwomp; // Start with 'resting' texture sides[sourceline->sidenum[0]].midtexture = sides[sourceline->sidenum[0]].bottomtexture; + + // interpolation + R_CreateInterpolator_SectorPlane(&thwomp->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&thwomp->thinker, sec, true); } /** Adds a thinker which checks if any MF_ENEMY objects with health are in the defined area. @@ -7285,6 +7305,22 @@ static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT3 s->last_height = sectors[control].floorheight + sectors[control].ceilingheight; s->affectee = affectee; P_AddThinker(THINK_MAIN, &s->thinker); + + // interpolation + switch (type) + { + case sc_side: + R_CreateInterpolator_SideScroll(&s->thinker, &sides[affectee]); + break; + case sc_floor: + R_CreateInterpolator_SectorScroll(&s->thinker, §ors[affectee], false); + break; + case sc_ceiling: + R_CreateInterpolator_SectorScroll(&s->thinker, §ors[affectee], true); + break; + default: + break; + } } /** Initializes the scrollers. diff --git a/src/p_telept.c b/src/p_telept.c index 434994ce6..59774dd76 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -171,7 +171,5 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle P_FlashPal(thing->player, PAL_MIXUP, 10); } - P_InitAngle(thing, angle); - return true; } diff --git a/src/p_tick.c b/src/p_tick.c index 521766979..8b777efbe 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -261,6 +261,7 @@ void P_RemoveThinkerDelayed(thinker_t *thinker) * thinker->prev->next = thinker->next */ (next->prev = currentthinker = thinker->prev)->next = next; + R_DestroyLevelInterpolators(thinker); Z_Free(thinker); } @@ -545,6 +546,8 @@ void P_Ticker(boolean run) if (run) { + R_UpdateMobjInterpolators(); + if (demo.recording) { G_WriteDemoExtraData(); @@ -737,6 +740,12 @@ void P_Ticker(boolean run) LUAh_PostThinkFrame(); } + if (run) + { + R_UpdateLevelInterpolators(); + R_UpdateViewInterpolation(); + } + P_MapEnd(); if (demo.playback) @@ -770,6 +779,8 @@ void P_PreTicker(INT32 frames) { P_MapStart(); + R_UpdateMobjInterpolators(); + // First loop: Ensure all players' distance to the finish line are all accurate for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) @@ -814,6 +825,10 @@ void P_PreTicker(INT32 frames) LUAh_PostThinkFrame(); + R_UpdateLevelInterpolators(); + R_UpdateViewInterpolation(); + R_ResetViewInterpolation(0); + P_MapEnd(); hook_defrosting--; diff --git a/src/p_user.c b/src/p_user.c index 8cc74c837..89f9149c4 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -20,6 +20,7 @@ #include "d_net.h" #include "g_game.h" #include "p_local.h" +#include "r_fps.h" #include "r_main.h" #include "s_sound.h" #include "r_skins.h" @@ -1204,12 +1205,12 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) } // Copy interpolation data :) - ghost->old_x = mobj->old_x; - ghost->old_y = mobj->old_y; - ghost->old_z = mobj->old_z; - ghost->old_angle = (mobj->player ? mobj->player->old_drawangle : mobj->old_angle); - ghost->old_pitch = mobj->old_pitch; - ghost->old_roll = mobj->old_roll; + ghost->old_x = mobj->old_x2; + ghost->old_y = mobj->old_y2; + ghost->old_z = mobj->old_z2; + ghost->old_angle = (mobj->player ? mobj->player->old_drawangle2 : mobj->old_angle2); + ghost->old_pitch = mobj->old_pitch2; + ghost->old_roll = mobj->old_roll2; return ghost; } diff --git a/src/r_bsp.c b/src/r_bsp.c index 6095f3739..7c198af1f 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -904,6 +904,7 @@ static void R_Subsector(size_t num) extracolormap_t *floorcolormap; extracolormap_t *ceilingcolormap; fixed_t floorcenterz, ceilingcenterz; + ffloor_t *rover; #ifdef RANGECHECK if (num >= numsubsectors) @@ -930,7 +931,23 @@ static void R_Subsector(size_t num) // Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps. if (frontsector->ffloors) { - if (frontsector->moved) + boolean anyMoved = frontsector->moved; + + if (anyMoved == false) + { + for (rover = frontsector->ffloors; rover; rover = rover->next) + { + sector_t *controlSec = §ors[rover->secnum]; + + if (controlSec->moved == true) + { + anyMoved = true; + break; + } + } + } + + if (anyMoved == true) { frontsector->numlights = sub->sector->numlights = 0; R_Prep3DFloors(frontsector); @@ -983,7 +1000,6 @@ static void R_Subsector(size_t num) ffloor[numffloors].polyobj = NULL; if (frontsector->ffloors) { - ffloor_t *rover; fixed_t heightcheck, planecenterz; for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next) diff --git a/src/r_fps.c b/src/r_fps.c index ea5fe38a7..61bc70624 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -20,42 +20,45 @@ #include "r_plane.h" #include "p_spec.h" #include "r_state.h" +#include "z_zone.h" #include "console.h" // con_startup_loadprogress #ifdef HWRENDER #include "hardware/hw_main.h" // for cv_glshearing #endif static CV_PossibleValue_t fpscap_cons_t[] = { - {-1, "Match refresh rate"}, - {0, "Unlimited"}, #ifdef DEVELOP // Lower values are actually pretty useful for debugging interp problems! - {1, "One Singular Frame"}, - {10, "10"}, - {20, "20"}, - {25, "25"}, - {30, "30"}, + {1, "MIN"}, +#else + {TICRATE, "MIN"}, #endif - {35, "35"}, - {50, "50"}, - {60, "60"}, - {70, "70"}, - {75, "75"}, - {90, "90"}, - {100, "100"}, - {120, "120"}, - {144, "144"}, - {200, "200"}, - {240, "240"}, + {300, "MAX"}, + {-1, "Unlimited"}, + {0, "Match refresh rate"}, {0, NULL} }; consvar_t cv_fpscap = CVAR_INIT ("fpscap", "Match refresh rate", CV_SAVE, fpscap_cons_t, NULL); UINT32 R_GetFramerateCap(void) { + if (rendermode == render_none) + { + // If we're not rendering (dedicated server), + // we shouldn't be using any interpolation. + return TICRATE; + } + + if (cv_fpscap.value == 0) + { + // 0: Match refresh rate + return I_GetRefreshRate(); + } + if (cv_fpscap.value < 0) { - return I_GetRefreshRate(); + // -1: Unlimited + return 0; } return cv_fpscap.value; @@ -63,7 +66,7 @@ UINT32 R_GetFramerateCap(void) boolean R_UsingFrameInterpolation(void) { - return (R_GetFramerateCap() != TICRATE); // maybe use ">" instead? + return (R_GetFramerateCap() != TICRATE || cv_timescale.value < FRACUNIT); } static viewvars_t pview_old[MAXSPLITSCREENPLAYERS]; @@ -72,18 +75,41 @@ static viewvars_t skyview_old[MAXSPLITSCREENPLAYERS]; static viewvars_t skyview_new[MAXSPLITSCREENPLAYERS]; static viewvars_t *oldview = &pview_old[0]; +static int oldview_invalid[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0}; viewvars_t *newview = &pview_new[0]; + enum viewcontext_e viewcontext = VIEWCONTEXT_PLAYER1; +static levelinterpolator_t **levelinterpolators; +static size_t levelinterpolators_len; +static size_t levelinterpolators_size; + + static fixed_t R_LerpFixed(fixed_t from, fixed_t to, fixed_t frac) { - return FixedMul(frac, to - from); + return from + FixedMul(frac, to - from); } static angle_t R_LerpAngle(angle_t from, angle_t to, fixed_t frac) { - return FixedMul(frac, to - from); + return from + FixedMul(frac, to - from); +} + +static vector2_t *R_LerpVector2(const vector2_t *from, const vector2_t *to, fixed_t frac, vector2_t *out) +{ + FV2_SubEx(to, from, out); + FV2_MulEx(out, frac, out); + FV2_AddEx(from, out, out); + return out; +} + +static vector3_t *R_LerpVector3(const vector3_t *from, const vector3_t *to, fixed_t frac, vector3_t *out) +{ + FV3_SubEx(to, from, out); + FV3_MulEx(out, frac, out); + FV3_AddEx(from, out, out); + return out; } // recalc necessary stuff for mouseaiming @@ -129,20 +155,44 @@ void R_InterpolateViewRollAngle(fixed_t frac) void R_InterpolateView(fixed_t frac) { - if (frac < 0) + viewvars_t* prevview = oldview; + UINT8 i; + + if (FIXED_TO_FLOAT(frac) < 0) frac = 0; -#if 0 if (frac > FRACUNIT) frac = FRACUNIT; -#endif - viewx = oldview->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); + if (viewcontext == VIEWCONTEXT_SKY1 || viewcontext == VIEWCONTEXT_PLAYER1) + { + i = 0; + } + else if (viewcontext == VIEWCONTEXT_SKY2 || viewcontext == VIEWCONTEXT_PLAYER2) + { + i = 1; + } + else if (viewcontext == VIEWCONTEXT_SKY3 || viewcontext == VIEWCONTEXT_PLAYER3) + { + i = 2; + } + else + { + i = 3; + } - viewangle = oldview->angle + R_LerpAngle(oldview->angle, newview->angle, frac); - aimingangle = oldview->aim + R_LerpAngle(oldview->aim, newview->aim, frac); - R_InterpolateViewRollAngle(frac); + if (oldview_invalid[i] != 0) + { + // interpolate from newview to newview + prevview = newview; + } + + viewx = R_LerpFixed(prevview->x, newview->x, frac); + viewy = R_LerpFixed(prevview->y, newview->y, frac); + viewz = R_LerpFixed(prevview->z, newview->z, frac); + + viewangle = R_LerpAngle(prevview->angle, newview->angle, frac); + aimingangle = R_LerpAngle(prevview->aim, newview->aim, frac); + viewroll = R_LerpAngle(prevview->roll, newview->roll, frac); viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); @@ -161,6 +211,23 @@ void R_UpdateViewInterpolation(void) { pview_old[i] = pview_new[i]; skyview_old[i] = skyview_new[i]; + if (oldview_invalid[i] > 0) oldview_invalid[i]--; + } +} + +void R_ResetViewInterpolation(UINT8 p) +{ + if (p == 0) + { + UINT8 i; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + oldview_invalid[i]++; + } + } + else + { + oldview_invalid[p - 1]++; } } @@ -203,7 +270,7 @@ fixed_t R_InterpolateFixed(fixed_t from, fixed_t to) return to; } - return (from + R_LerpFixed(from, to, rendertimefrac)); + return (R_LerpFixed(from, to, rendertimefrac)); } angle_t R_InterpolateAngle(angle_t from, angle_t to) @@ -213,5 +280,537 @@ angle_t R_InterpolateAngle(angle_t from, angle_t to) return to; } - return (from + R_LerpAngle(from, to, rendertimefrac)); + return (R_LerpAngle(from, to, rendertimefrac)); +} + +void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out) +{ + if (frac == FRACUNIT) + { + out->x = mobj->x; + out->y = mobj->y; + out->z = mobj->z; + out->scale = mobj->scale; + out->subsector = mobj->subsector; + out->angle = mobj->player ? mobj->player->drawangle : mobj->angle; + out->spritexscale = mobj->spritexscale; + out->spriteyscale = mobj->spriteyscale; + out->spritexoffset = mobj->spritexoffset; + out->spriteyoffset = mobj->spriteyoffset; + return; + } + + out->x = R_LerpFixed(mobj->old_x, mobj->x, frac); + out->y = R_LerpFixed(mobj->old_y, mobj->y, frac); + out->z = R_LerpFixed(mobj->old_z, mobj->z, frac); + out->scale = mobj->resetinterp ? mobj->scale : R_LerpFixed(mobj->old_scale, mobj->scale, frac); + out->spritexscale = mobj->resetinterp ? mobj->spritexscale : R_LerpFixed(mobj->old_spritexscale, mobj->spritexscale, frac); + out->spriteyscale = mobj->resetinterp ? mobj->spriteyscale : R_LerpFixed(mobj->old_spriteyscale, mobj->spriteyscale, frac); + + // Sprite offsets are not interpolated until we have a way to interpolate them explicitly in Lua. + // It seems existing mods visually break more often than not if it is interpolated. + out->spritexoffset = mobj->spritexoffset; + out->spriteyoffset = mobj->spriteyoffset; + + out->subsector = R_PointInSubsector(out->x, out->y); + + if (mobj->player) + { + out->angle = mobj->resetinterp ? mobj->player->drawangle : R_LerpAngle(mobj->player->old_drawangle, mobj->player->drawangle, frac); + } + else + { + out->angle = mobj->resetinterp ? mobj->angle : R_LerpAngle(mobj->old_angle, mobj->angle, frac); + } +} + +void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjstate_t *out) +{ + if (frac == FRACUNIT) + { + out->x = mobj->x; + out->y = mobj->y; + out->z = mobj->z; + out->scale = FRACUNIT; + out->subsector = mobj->subsector; + out->angle = mobj->angle; + out->spritexscale = mobj->spritexscale; + out->spriteyscale = mobj->spriteyscale; + out->spritexoffset = mobj->spritexoffset; + out->spriteyoffset = mobj->spriteyoffset; + return; + } + + out->x = R_LerpFixed(mobj->old_x, mobj->x, frac); + out->y = R_LerpFixed(mobj->old_y, mobj->y, frac); + out->z = R_LerpFixed(mobj->old_z, mobj->z, frac); + out->scale = FRACUNIT; + out->spritexscale = R_LerpFixed(mobj->old_spritexscale, mobj->spritexscale, frac); + out->spriteyscale = R_LerpFixed(mobj->old_spriteyscale, mobj->spriteyscale, frac); + out->spritexoffset = R_LerpFixed(mobj->old_spritexoffset, mobj->spritexoffset, frac); + out->spriteyoffset = R_LerpFixed(mobj->old_spriteyoffset, mobj->spriteyoffset, frac); + + out->subsector = R_PointInSubsector(out->x, out->y); + + out->angle = R_LerpAngle(mobj->old_angle, mobj->angle, frac); +} + +static void AddInterpolator(levelinterpolator_t* interpolator) +{ + if (levelinterpolators_len >= levelinterpolators_size) + { + if (levelinterpolators_size == 0) + { + levelinterpolators_size = 128; + } + else + { + levelinterpolators_size *= 2; + } + + levelinterpolators = Z_ReallocAlign( + (void*) levelinterpolators, + sizeof(levelinterpolator_t*) * levelinterpolators_size, + PU_LEVEL, + NULL, + sizeof(levelinterpolator_t*) * 8 + ); + } + + levelinterpolators[levelinterpolators_len] = interpolator; + levelinterpolators_len += 1; +} + +static levelinterpolator_t *CreateInterpolator(levelinterpolator_type_e type, thinker_t *thinker) +{ + levelinterpolator_t *ret = (levelinterpolator_t*) Z_CallocAlign( + sizeof(levelinterpolator_t), + PU_LEVEL, + NULL, + sizeof(levelinterpolator_t) * 8 + ); + + ret->type = type; + ret->thinker = thinker; + + AddInterpolator(ret); + + return ret; +} + +void R_CreateInterpolator_SectorPlane(thinker_t *thinker, sector_t *sector, boolean ceiling) +{ + levelinterpolator_t *interp = CreateInterpolator(LVLINTERP_SectorPlane, thinker); + interp->sectorplane.sector = sector; + interp->sectorplane.ceiling = ceiling; + if (ceiling) + { + interp->sectorplane.oldheight = interp->sectorplane.bakheight = sector->ceilingheight; + } + else + { + interp->sectorplane.oldheight = interp->sectorplane.bakheight = sector->floorheight; + } +} + +void R_CreateInterpolator_SectorScroll(thinker_t *thinker, sector_t *sector, boolean ceiling) +{ + levelinterpolator_t *interp = CreateInterpolator(LVLINTERP_SectorScroll, thinker); + interp->sectorscroll.sector = sector; + interp->sectorscroll.ceiling = ceiling; + if (ceiling) + { + interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->ceiling_xoffs; + interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->ceiling_yoffs; + } + else + { + interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->floor_xoffs; + interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->floor_yoffs; + } +} + +void R_CreateInterpolator_SideScroll(thinker_t *thinker, side_t *side) +{ + levelinterpolator_t *interp = CreateInterpolator(LVLINTERP_SideScroll, thinker); + interp->sidescroll.side = side; + interp->sidescroll.oldtextureoffset = interp->sidescroll.baktextureoffset = side->textureoffset; + interp->sidescroll.oldrowoffset = interp->sidescroll.bakrowoffset = side->rowoffset; +} + +void R_CreateInterpolator_Polyobj(thinker_t *thinker, polyobj_t *polyobj) +{ + levelinterpolator_t *interp = CreateInterpolator(LVLINTERP_Polyobj, thinker); + interp->polyobj.polyobj = polyobj; + interp->polyobj.vertices_size = polyobj->numVertices; + + interp->polyobj.oldvertices = Z_CallocAlign(sizeof(fixed_t) * 2 * polyobj->numVertices, PU_LEVEL, NULL, 32); + interp->polyobj.bakvertices = Z_CallocAlign(sizeof(fixed_t) * 2 * polyobj->numVertices, PU_LEVEL, NULL, 32); + for (size_t i = 0; i < polyobj->numVertices; i++) + { + interp->polyobj.oldvertices[i * 2 ] = interp->polyobj.bakvertices[i * 2 ] = polyobj->vertices[i]->x; + interp->polyobj.oldvertices[i * 2 + 1] = interp->polyobj.bakvertices[i * 2 + 1] = polyobj->vertices[i]->y; + } + + interp->polyobj.oldcx = interp->polyobj.bakcx = polyobj->centerPt.x; + interp->polyobj.oldcy = interp->polyobj.bakcy = polyobj->centerPt.y; +} + +void R_CreateInterpolator_DynSlope(thinker_t *thinker, pslope_t *slope) +{ + levelinterpolator_t *interp = CreateInterpolator(LVLINTERP_DynSlope, thinker); + interp->dynslope.slope = slope; + + FV3_Copy(&interp->dynslope.oldo, &slope->o); + FV3_Copy(&interp->dynslope.bako, &slope->o); + + FV2_Copy(&interp->dynslope.oldd, &slope->d); + FV2_Copy(&interp->dynslope.bakd, &slope->d); + + interp->dynslope.oldzdelta = interp->dynslope.bakzdelta = slope->zdelta; +} + +void R_InitializeLevelInterpolators(void) +{ + levelinterpolators_len = 0; + levelinterpolators_size = 0; + levelinterpolators = NULL; +} + +static void UpdateLevelInterpolatorState(levelinterpolator_t *interp) +{ + size_t i; + + switch (interp->type) + { + case LVLINTERP_SectorPlane: + interp->sectorplane.oldheight = interp->sectorplane.bakheight; + interp->sectorplane.bakheight = interp->sectorplane.ceiling ? interp->sectorplane.sector->ceilingheight : interp->sectorplane.sector->floorheight; + break; + case LVLINTERP_SectorScroll: + interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs; + interp->sectorscroll.bakxoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_xoffs : interp->sectorscroll.sector->floor_xoffs; + interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs; + interp->sectorscroll.bakyoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_yoffs : interp->sectorscroll.sector->floor_yoffs; + break; + case LVLINTERP_SideScroll: + interp->sidescroll.oldtextureoffset = interp->sidescroll.baktextureoffset; + interp->sidescroll.baktextureoffset = interp->sidescroll.side->textureoffset; + interp->sidescroll.oldrowoffset = interp->sidescroll.bakrowoffset; + interp->sidescroll.bakrowoffset = interp->sidescroll.side->rowoffset; + break; + case LVLINTERP_Polyobj: + for (i = 0; i < interp->polyobj.vertices_size; i++) + { + interp->polyobj.oldvertices[i * 2 ] = interp->polyobj.bakvertices[i * 2 ]; + interp->polyobj.oldvertices[i * 2 + 1] = interp->polyobj.bakvertices[i * 2 + 1]; + interp->polyobj.bakvertices[i * 2 ] = interp->polyobj.polyobj->vertices[i]->x; + interp->polyobj.bakvertices[i * 2 + 1] = interp->polyobj.polyobj->vertices[i]->y; + } + interp->polyobj.oldcx = interp->polyobj.bakcx; + interp->polyobj.oldcy = interp->polyobj.bakcy; + interp->polyobj.bakcx = interp->polyobj.polyobj->centerPt.x; + interp->polyobj.bakcy = interp->polyobj.polyobj->centerPt.y; + break; + case LVLINTERP_DynSlope: + FV3_Copy(&interp->dynslope.oldo, &interp->dynslope.bako); + FV2_Copy(&interp->dynslope.oldd, &interp->dynslope.bakd); + interp->dynslope.oldzdelta = interp->dynslope.bakzdelta; + + FV3_Copy(&interp->dynslope.bako, &interp->dynslope.slope->o); + FV2_Copy(&interp->dynslope.bakd, &interp->dynslope.slope->d); + interp->dynslope.bakzdelta = interp->dynslope.slope->zdelta; + break; + } +} + +void R_UpdateLevelInterpolators(void) +{ + size_t i; + + for (i = 0; i < levelinterpolators_len; i++) + { + levelinterpolator_t *interp = levelinterpolators[i]; + + UpdateLevelInterpolatorState(interp); + } +} + +void R_ClearLevelInterpolatorState(thinker_t *thinker) +{ + size_t i; + + for (i = 0; i < levelinterpolators_len; i++) + { + levelinterpolator_t *interp = levelinterpolators[i]; + + if (interp->thinker == thinker) + { + // Do it twice to make the old state match the new + UpdateLevelInterpolatorState(interp); + UpdateLevelInterpolatorState(interp); + } + } +} + +void R_ApplyLevelInterpolators(fixed_t frac) +{ + size_t i, ii; + + for (i = 0; i < levelinterpolators_len; i++) + { + levelinterpolator_t *interp = levelinterpolators[i]; + + switch (interp->type) + { + case LVLINTERP_SectorPlane: + if (interp->sectorplane.ceiling) + { + interp->sectorplane.sector->ceilingheight = R_LerpFixed(interp->sectorplane.oldheight, interp->sectorplane.bakheight, frac); + } + else + { + interp->sectorplane.sector->floorheight = R_LerpFixed(interp->sectorplane.oldheight, interp->sectorplane.bakheight, frac); + } + interp->sectorplane.sector->moved = true; + break; + case LVLINTERP_SectorScroll: + if (interp->sectorscroll.ceiling) + { + interp->sectorscroll.sector->ceiling_xoffs = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac); + interp->sectorscroll.sector->ceiling_yoffs = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac); + } + else + { + interp->sectorscroll.sector->floor_xoffs = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac); + interp->sectorscroll.sector->floor_yoffs = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac); + } + break; + case LVLINTERP_SideScroll: + interp->sidescroll.side->textureoffset = R_LerpFixed(interp->sidescroll.oldtextureoffset, interp->sidescroll.baktextureoffset, frac); + interp->sidescroll.side->rowoffset = R_LerpFixed(interp->sidescroll.oldrowoffset, interp->sidescroll.bakrowoffset, frac); + break; + case LVLINTERP_Polyobj: + for (ii = 0; ii < interp->polyobj.vertices_size; ii++) + { + interp->polyobj.polyobj->vertices[ii]->x = R_LerpFixed(interp->polyobj.oldvertices[ii * 2 ], interp->polyobj.bakvertices[ii * 2 ], frac); + interp->polyobj.polyobj->vertices[ii]->y = R_LerpFixed(interp->polyobj.oldvertices[ii * 2 + 1], interp->polyobj.bakvertices[ii * 2 + 1], frac); + } + interp->polyobj.polyobj->centerPt.x = R_LerpFixed(interp->polyobj.oldcx, interp->polyobj.bakcx, frac); + interp->polyobj.polyobj->centerPt.y = R_LerpFixed(interp->polyobj.oldcy, interp->polyobj.bakcy, frac); + break; + case LVLINTERP_DynSlope: + R_LerpVector3(&interp->dynslope.oldo, &interp->dynslope.bako, frac, &interp->dynslope.slope->o); + R_LerpVector2(&interp->dynslope.oldd, &interp->dynslope.bakd, frac, &interp->dynslope.slope->d); + interp->dynslope.slope->zdelta = R_LerpFixed(interp->dynslope.oldzdelta, interp->dynslope.bakzdelta, frac); + break; + } + } +} + +void R_RestoreLevelInterpolators(void) +{ + size_t i, ii; + + for (i = 0; i < levelinterpolators_len; i++) + { + levelinterpolator_t *interp = levelinterpolators[i]; + + switch (interp->type) + { + case LVLINTERP_SectorPlane: + if (interp->sectorplane.ceiling) + { + interp->sectorplane.sector->ceilingheight = interp->sectorplane.bakheight; + } + else + { + interp->sectorplane.sector->floorheight = interp->sectorplane.bakheight; + } + interp->sectorplane.sector->moved = true; + break; + case LVLINTERP_SectorScroll: + if (interp->sectorscroll.ceiling) + { + interp->sectorscroll.sector->ceiling_xoffs = interp->sectorscroll.bakxoffs; + interp->sectorscroll.sector->ceiling_yoffs = interp->sectorscroll.bakyoffs; + } + else + { + interp->sectorscroll.sector->floor_xoffs = interp->sectorscroll.bakxoffs; + interp->sectorscroll.sector->floor_yoffs = interp->sectorscroll.bakyoffs; + } + break; + case LVLINTERP_SideScroll: + interp->sidescroll.side->textureoffset = interp->sidescroll.baktextureoffset; + interp->sidescroll.side->rowoffset = interp->sidescroll.bakrowoffset; + break; + case LVLINTERP_Polyobj: + for (ii = 0; ii < interp->polyobj.vertices_size; ii++) + { + interp->polyobj.polyobj->vertices[ii]->x = interp->polyobj.bakvertices[ii * 2 ]; + interp->polyobj.polyobj->vertices[ii]->y = interp->polyobj.bakvertices[ii * 2 + 1]; + } + interp->polyobj.polyobj->centerPt.x = interp->polyobj.bakcx; + interp->polyobj.polyobj->centerPt.y = interp->polyobj.bakcy; + break; + case LVLINTERP_DynSlope: + FV3_Copy(&interp->dynslope.slope->o, &interp->dynslope.bako); + FV2_Copy(&interp->dynslope.slope->d, &interp->dynslope.bakd); + interp->dynslope.slope->zdelta = interp->dynslope.bakzdelta; + break; + } + } +} + +void R_DestroyLevelInterpolators(thinker_t *thinker) +{ + size_t i; + + for (i = 0; i < levelinterpolators_len; i++) + { + levelinterpolator_t *interp = levelinterpolators[i]; + + if (interp->thinker == thinker) + { + // Swap the tail of the level interpolators to this spot + levelinterpolators[i] = levelinterpolators[levelinterpolators_len - 1]; + levelinterpolators_len -= 1; + + Z_Free(interp); + i -= 1; + } + } +} + +static mobj_t **interpolated_mobjs = NULL; +static size_t interpolated_mobjs_len = 0; +static size_t interpolated_mobjs_capacity = 0; + +// NOTE: This will NOT check that the mobj has already been added, for perf +// reasons. +void R_AddMobjInterpolator(mobj_t *mobj) +{ + if (interpolated_mobjs_len >= interpolated_mobjs_capacity) + { + if (interpolated_mobjs_capacity == 0) + { + interpolated_mobjs_capacity = 256; + } + else + { + interpolated_mobjs_capacity *= 2; + } + + interpolated_mobjs = Z_ReallocAlign( + interpolated_mobjs, + sizeof(mobj_t *) * interpolated_mobjs_capacity, + PU_LEVEL, + NULL, + 64 + ); + } + + interpolated_mobjs[interpolated_mobjs_len] = mobj; + interpolated_mobjs_len += 1; + + R_ResetMobjInterpolationState(mobj); + mobj->resetinterp = true; +} + +void R_RemoveMobjInterpolator(mobj_t *mobj) +{ + size_t i; + + if (interpolated_mobjs_len == 0) return; + + for (i = 0; i < interpolated_mobjs_len - 1; i++) + { + if (interpolated_mobjs[i] == mobj) + { + interpolated_mobjs[i] = interpolated_mobjs[ + interpolated_mobjs_len - 1 + ]; + interpolated_mobjs_len -= 1; + return; + } + } +} + +void R_InitMobjInterpolators(void) +{ + // apparently it's not acceptable to free something already unallocated + // Z_Free(interpolated_mobjs); + interpolated_mobjs = NULL; + interpolated_mobjs_len = 0; + interpolated_mobjs_capacity = 0; +} + +void R_UpdateMobjInterpolators(void) +{ + size_t i; + for (i = 0; i < interpolated_mobjs_len; i++) + { + mobj_t *mobj = interpolated_mobjs[i]; + if (!P_MobjWasRemoved(mobj)) + R_ResetMobjInterpolationState(mobj); + } +} + +// +// P_ResetMobjInterpolationState +// +// Reset the rendering interpolation state of the mobj. +// +void R_ResetMobjInterpolationState(mobj_t *mobj) +{ + mobj->old_x2 = mobj->old_x; + mobj->old_y2 = mobj->old_y; + mobj->old_z2 = mobj->old_z; + mobj->old_angle2 = mobj->old_angle; + mobj->old_pitch2 = mobj->old_pitch; + mobj->old_roll2 = mobj->old_roll; + mobj->old_scale2 = mobj->old_scale; + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + mobj->old_angle = mobj->angle; + mobj->old_pitch = mobj->pitch; + mobj->old_roll = mobj->roll; + mobj->old_scale = mobj->scale; + mobj->old_spritexscale = mobj->spritexscale; + mobj->old_spriteyscale = mobj->spriteyscale; + mobj->old_spritexoffset = mobj->spritexoffset; + mobj->old_spriteyoffset = mobj->spriteyoffset; + + if (mobj->player) + { + mobj->player->old_drawangle2 = mobj->player->old_drawangle; + mobj->player->old_drawangle = mobj->player->drawangle; + } + + mobj->resetinterp = false; +} + +// +// P_ResetPrecipitationMobjInterpolationState +// +// Reset the rendering interpolation state of the precipmobj. +// +void R_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj) +{ + mobj->old_x2 = mobj->old_x; + mobj->old_y2 = mobj->old_y; + mobj->old_z2 = mobj->old_z; + mobj->old_angle2 = mobj->old_angle; + mobj->old_pitch2 = mobj->old_pitch; + mobj->old_roll2 = mobj->old_roll; + mobj->old_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + mobj->old_angle = mobj->angle; + mobj->old_spritexscale = mobj->spritexscale; + mobj->old_spriteyscale = mobj->spriteyscale; + mobj->old_spritexoffset = mobj->spritexoffset; + mobj->old_spriteyoffset = mobj->spriteyoffset; } diff --git a/src/r_fps.h b/src/r_fps.h index 952c3c94e..a91f7004e 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -54,16 +54,112 @@ typedef struct { extern viewvars_t *newview; +typedef struct { + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t *subsector; + angle_t angle; + fixed_t scale; + fixed_t spritexscale; + fixed_t spriteyscale; + fixed_t spritexoffset; + fixed_t spriteyoffset; +} interpmobjstate_t; + +// Level interpolators + +// The union tag for levelinterpolator_t +typedef enum { + LVLINTERP_SectorPlane, + LVLINTERP_SectorScroll, + LVLINTERP_SideScroll, + LVLINTERP_Polyobj, + LVLINTERP_DynSlope, +} levelinterpolator_type_e; + +// Tagged union of a level interpolator +typedef struct levelinterpolator_s { + levelinterpolator_type_e type; + thinker_t *thinker; + union { + struct { + sector_t *sector; + fixed_t oldheight; + fixed_t bakheight; + boolean ceiling; + } sectorplane; + struct { + sector_t *sector; + fixed_t oldxoffs, oldyoffs, bakxoffs, bakyoffs; + boolean ceiling; + } sectorscroll; + struct { + side_t *side; + fixed_t oldtextureoffset, oldrowoffset, baktextureoffset, bakrowoffset; + } sidescroll; + struct { + polyobj_t *polyobj; + fixed_t *oldvertices; + fixed_t *bakvertices; + size_t vertices_size; + fixed_t oldcx, oldcy, bakcx, bakcy; + } polyobj; + struct { + pslope_t *slope; + vector3_t oldo, bako; + vector2_t oldd, bakd; + fixed_t oldzdelta, bakzdelta; + } dynslope; + }; +} levelinterpolator_t; + // Interpolates the current view variables (r_state.h) against the selected view context in R_SetViewContext void R_InterpolateView(fixed_t frac); // Special function just for software void R_InterpolateViewRollAngle(fixed_t frac); // Buffer the current new views into the old views. Call once after each real tic. void R_UpdateViewInterpolation(void); +// Reset the view states (e.g. after level load) so R_InterpolateView doesn't interpolate invalid data +void R_ResetViewInterpolation(UINT8 p); // Set the current view context (the viewvars pointed to by newview) void R_SetViewContext(enum viewcontext_e _viewcontext); fixed_t R_InterpolateFixed(fixed_t from, fixed_t to); angle_t R_InterpolateAngle(angle_t from, angle_t to); +// Evaluate the interpolated mobj state for the given mobj +void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out); +// Evaluate the interpolated mobj state for the given precipmobj +void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjstate_t *out); + +void R_CreateInterpolator_SectorPlane(thinker_t *thinker, sector_t *sector, boolean ceiling); +void R_CreateInterpolator_SectorScroll(thinker_t *thinker, sector_t *sector, boolean ceiling); +void R_CreateInterpolator_SideScroll(thinker_t *thinker, side_t *side); +void R_CreateInterpolator_Polyobj(thinker_t *thinker, polyobj_t *polyobj); +void R_CreateInterpolator_DynSlope(thinker_t *thinker, pslope_t *slope); + +// Initialize level interpolators after a level change +void R_InitializeLevelInterpolators(void); +// Update level interpolators, storing the previous and current states. +void R_UpdateLevelInterpolators(void); +// Clear states for all level interpolators for the thinker +void R_ClearLevelInterpolatorState(thinker_t *thinker); +// Apply level interpolators to the actual game state +void R_ApplyLevelInterpolators(fixed_t frac); +// Restore level interpolators to the real game state +void R_RestoreLevelInterpolators(void); +// Destroy interpolators associated with a thinker +void R_DestroyLevelInterpolators(thinker_t *thinker); + +// Initialize internal mobj interpolator list (e.g. during level loading) +void R_InitMobjInterpolators(void); +// Add interpolation state for the given mobj +void R_AddMobjInterpolator(mobj_t *mobj); +// Remove the interpolation state for the given mobj +void R_RemoveMobjInterpolator(mobj_t *mobj); +void R_UpdateMobjInterpolators(void); +void R_ResetMobjInterpolationState(mobj_t *mobj); +void R_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj); + #endif diff --git a/src/r_main.c b/src/r_main.c index 0288c585a..85147fe07 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -82,6 +82,7 @@ int r_splitscreen; fixed_t rendertimefrac; fixed_t renderdeltatics; +boolean renderisnewtic; // // precalculated math tables diff --git a/src/r_main.h b/src/r_main.h index b67eb8c4f..e7f5b9d4c 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -37,6 +37,8 @@ extern size_t validcount, linecount, loopcount, framecount; extern fixed_t rendertimefrac; // Evaluated delta tics for this frame (how many tics since the last frame) extern fixed_t renderdeltatics; +// The current render is a new logical tic +extern boolean renderisnewtic; // // Lighting LUT. diff --git a/src/r_things.c b/src/r_things.c index 9441804b1..1a68415ee 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -23,6 +23,7 @@ #include "info.h" // spr2names #include "i_video.h" // rendermode #include "i_system.h" +#include "r_fps.h" #include "r_things.h" #include "r_patch.h" #include "r_patchrotation.h" @@ -1176,19 +1177,34 @@ static void R_SplitSprite(vissprite_t *sprite) // shadowslope is filled with the floor's slope, if provided // fixed_t R_GetShadowZ( - mobj_t *thing, pslope_t **shadowslope, - fixed_t interpx, fixed_t interpy, fixed_t interpz) + mobj_t *thing, pslope_t **shadowslope) { - fixed_t halfHeight = interpz + (thing->height >> 1); + fixed_t halfHeight; boolean isflipped = thing->eflags & MFE_VERTICALFLIP; - fixed_t floorz = P_GetFloorZ(thing, thing->subsector->sector, interpx, interpy, NULL); - fixed_t ceilingz = P_GetCeilingZ(thing, thing->subsector->sector, interpx, interpy, NULL); + fixed_t floorz; + fixed_t ceilingz; fixed_t z, groundz = isflipped ? INT32_MAX : INT32_MIN; pslope_t *slope, *groundslope = NULL; msecnode_t *node; sector_t *sector; ffloor_t *rover; + // for frame interpolation + interpmobjstate_t interp = {0}; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + halfHeight = interp.z + (thing->height >> 1); + floorz = P_GetFloorZ(thing, interp.subsector->sector, interp.x, interp.y, NULL); + ceilingz = P_GetCeilingZ(thing, interp.subsector->sector, interp.x, interp.y, NULL); + #define CHECKZ (isflipped ? z > halfHeight && z < groundz : z < halfHeight && z > groundz) for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) @@ -1200,7 +1216,7 @@ fixed_t R_GetShadowZ( if (sector->heightsec != -1) z = isflipped ? sectors[sector->heightsec].ceilingheight : sectors[sector->heightsec].floorheight; else - z = isflipped ? P_GetSectorCeilingZAt(sector, interpx, interpy) : P_GetSectorFloorZAt(sector, interpx, interpy); + z = isflipped ? P_GetSectorCeilingZAt(sector, interp.x, interp.y) : P_GetSectorFloorZAt(sector, interp.x, interp.y); if CHECKZ { @@ -1214,8 +1230,7 @@ fixed_t R_GetShadowZ( if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE))) continue; - z = isflipped ? P_GetFFloorBottomZAt(rover, interpx, interpy) : P_GetFFloorTopZAt(rover, interpx, interpy); - + z = isflipped ? P_GetFFloorBottomZAt(rover, interp.x, interp.y) : P_GetFFloorTopZAt(rover, interp.x, interp.y); if CHECKZ { groundz = z; @@ -1231,65 +1246,6 @@ fixed_t R_GetShadowZ( groundslope = NULL; } -#if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7. - // NOTE: this section was not updated to reflect reverse gravity support - // Check polyobjects and see if groundz needs to be altered, for rings only because they don't update floorz - if (thing->type == MT_RING) - { - INT32 xl, xh, yl, yh, bx, by; - - xl = (unsigned)(interpx - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(interpx + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(interpy - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(interpy + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - validcount++; - - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - { - INT32 offset; - polymaplink_t *plink; // haleyjd 02/22/06 - - if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight) - continue; - - offset = by*bmapwidth + bx; - - // haleyjd 02/22/06: consider polyobject lines - plink = polyblocklinks[offset]; - - while (plink) - { - polyobj_t *po = plink->po; - - if (po->validcount != validcount) // if polyobj hasn't been checked - { - po->validcount = validcount; - - if (!P_MobjInsidePolyobj(po, thing) || !(po->flags & POF_RENDERPLANES)) - { - plink = (polymaplink_t *)(plink->link.next); - continue; - } - - // We're inside it! Yess... - z = po->lines[0]->backsector->ceilingheight; - - if (z < halfHeight && z > groundz) - { - groundz = z; - groundslope = NULL; - } - } - plink = (polymaplink_t *)(plink->link.next); - } - } - } -#endif - if (shadowslope != NULL) *shadowslope = groundslope; @@ -1301,12 +1257,26 @@ fixed_t R_GetShadowZ( static void R_SkewShadowSprite( mobj_t *thing, pslope_t *groundslope, fixed_t groundz, INT32 spriteheight, fixed_t scalemul, - fixed_t *shadowyscale, fixed_t *shadowskew, - fixed_t interpx, fixed_t interpy) + fixed_t *shadowyscale, fixed_t *shadowskew) { + // haha let's try some dumb stuff fixed_t xslope, zslope; - angle_t sloperelang = (R_PointToAngle(interpx, interpy) - groundslope->xydirection) >> ANGLETOFINESHIFT; + angle_t sloperelang; + + // for frame interpolation + interpmobjstate_t interp = {0}; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + sloperelang = (R_PointToAngle(interp.x, interp.y) - groundslope->xydirection) >> ANGLETOFINESHIFT; xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta); zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta); @@ -1324,8 +1294,7 @@ static void R_SkewShadowSprite( static void R_ProjectDropShadow( mobj_t *thing, vissprite_t *vis, - fixed_t scale, fixed_t tx, fixed_t tz, - fixed_t interpx, fixed_t interpy, fixed_t interpz) + fixed_t scale, fixed_t tx, fixed_t tz) { vissprite_t *shadow; patch_t *patch; @@ -1333,11 +1302,21 @@ static void R_ProjectDropShadow( INT32 light = 0; fixed_t groundz; pslope_t *groundslope; + interpmobjstate_t interp = {0}; - groundz = R_GetShadowZ(thing, &groundslope, interpx, interpy, interpz); + groundz = R_GetShadowZ(thing, &groundslope); if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + patch = W_CachePatchName("DSHADOW", PU_SPRITE); xscale = FixedDiv(projection[viewssnum], tz); yscale = FixedDiv(projectiony[viewssnum], tz); @@ -1353,8 +1332,7 @@ static void R_ProjectDropShadow( thing, groundslope, groundz, patch->height, FRACUNIT, - &shadowyscale, &shadowskew, - interpx, interpy); + &shadowyscale, &shadowskew); } tx -= patch->width * shadowxscale/2; @@ -1374,11 +1352,11 @@ static void R_ProjectDropShadow( shadow->mobjflags = 0; shadow->sortscale = vis->sortscale; shadow->dispoffset = vis->dispoffset - 5; - shadow->gx = interpx; - shadow->gy = interpy; + shadow->gx = interp.x; + shadow->gy = interp.y; shadow->gzt = groundz + patch->height * shadowyscale / 2; shadow->gz = shadow->gzt - patch->height * shadowyscale; - shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale)); + shadow->texturemid = FixedMul(interp.scale, FixedDiv(shadow->gzt - viewz, shadowyscale)); if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES) shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale); shadow->scalestep = 0; @@ -1391,7 +1369,7 @@ static void R_ProjectDropShadow( shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 shadow->scale = FixedMul(yscale, shadowyscale); - shadow->thingscale = thing->scale; + shadow->thingscale = interp.scale; shadow->sector = vis->sector; shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS); shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS); @@ -1419,7 +1397,7 @@ static void R_ProjectDropShadow( // 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], interp.x, interp.y); if (h <= shadow->gzt) { light = lightnum - 1; break; @@ -1499,7 +1477,7 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t gz = 0, gzt = 0; INT32 heightsec, phs; INT32 light = 0; - fixed_t this_scale = thing->scale; + fixed_t this_scale; fixed_t spritexscale, spriteyscale; // rotsprite @@ -1513,20 +1491,20 @@ static void R_ProjectSprite(mobj_t *thing) #endif // uncapped/interpolation - fixed_t interpx = R_InterpolateFixed(thing->old_x, thing->x); - fixed_t interpy = R_InterpolateFixed(thing->old_y, thing->y); - fixed_t interpz = R_InterpolateFixed(thing->old_z, thing->z); - angle_t interpangle = ANGLE_MAX; + interpmobjstate_t interp = {0}; - if (thing->player) + // do interpolation + if (R_UsingFrameInterpolation() && !paused) { - interpangle = R_InterpolateAngle(thing->player->old_drawangle, thing->player->drawangle); + R_InterpolateMobjState(oldthing, rendertimefrac, &interp); } else { - interpangle = R_InterpolateAngle(thing->old_angle, thing->angle); + R_InterpolateMobjState(oldthing, FRACUNIT, &interp); } + this_scale = interp.scale; + // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) { @@ -1537,19 +1515,19 @@ static void R_ProjectSprite(mobj_t *thing) mul = -mul; } - interpx += FixedMul(thing->momx, mul); - interpy += FixedMul(thing->momy, mul); - interpz += FixedMul(thing->momz, mul); + interp.x += FixedMul(thing->momx, mul); + interp.y += FixedMul(thing->momy, mul); + interp.z += FixedMul(thing->momz, mul); } // sprite offset - interpx += thing->sprxoff; - interpy += thing->spryoff; - interpz += thing->sprzoff; + interp.x += thing->sprxoff; + interp.y += thing->spryoff; + interp.z += thing->sprzoff; // transform the origin point - tr_x = interpx - viewx; - tr_y = interpy - viewy; + tr_x = interp.x - viewx; + tr_y = interp.y - viewy; basetz = tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance @@ -1626,7 +1604,7 @@ static void R_ProjectSprite(mobj_t *thing) if (sprframe->rotate != SRF_SINGLE || papersprite) { - ang = R_PointToAngle (interpx, interpy) - interpangle; + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if (mirrored) ang = InvAngle(ang); } @@ -1641,7 +1619,7 @@ static void R_ProjectSprite(mobj_t *thing) else { // choose a different rotation based on player view - //ang = R_PointToAngle (interpx, interpy) - interpangle; + //ang = R_PointToAngle (interp.x, interp.y) - interpangle; if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right rot = 6; // F7 slot @@ -1704,15 +1682,15 @@ static void R_ProjectSprite(mobj_t *thing) flip = !flip != !hflip; // calculate edges of the shape - spritexscale = thing->spritexscale; - spriteyscale = thing->spriteyscale; + spritexscale = interp.spritexscale; + spriteyscale = interp.spriteyscale; if (spritexscale < 1 || spriteyscale < 1) return; if (thing->renderflags & RF_ABSOLUTEOFFSETS) { - spr_offset = thing->spritexoffset; - spr_topoffset = thing->spriteyoffset; + spr_offset = interp.spritexoffset; + spr_topoffset = interp.spriteyoffset; } else { @@ -1721,8 +1699,8 @@ static void R_ProjectSprite(mobj_t *thing) if ((thing->renderflags & RF_FLIPOFFSETS) && flip) flipoffset = -1; - spr_offset += thing->spritexoffset * flipoffset; - spr_topoffset += thing->spriteyoffset * flipoffset; + spr_offset += interp.spritexoffset * flipoffset; + spr_topoffset += interp.spriteyoffset * flipoffset; } if (flip) @@ -1744,8 +1722,8 @@ static void R_ProjectSprite(mobj_t *thing) offset2 *= -1; } - cosmul = FINECOSINE(thing->angle>>ANGLETOFINESHIFT); - sinmul = FINESINE(thing->angle>>ANGLETOFINESHIFT); + cosmul = FINECOSINE(interp.angle >> ANGLETOFINESHIFT); + sinmul = FINESINE(interp.angle >> ANGLETOFINESHIFT); tr_x += FixedMul(offset, cosmul); tr_y += FixedMul(offset, sinmul); @@ -1761,7 +1739,7 @@ static void R_ProjectSprite(mobj_t *thing) paperoffset = -paperoffset; paperdistance = -paperdistance; } - centerangle = viewangle - thing->angle; + centerangle = viewangle - interp.angle; tr_x += FixedMul(offset2, cosmul); tr_y += FixedMul(offset2, sinmul); @@ -1853,13 +1831,19 @@ static void R_ProjectSprite(mobj_t *thing) if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY) { + interpmobjstate_t tracer_interp = {0}; fixed_t linkscale; thing = thing->tracer; - interpx = R_InterpolateFixed(thing->old_x, thing->x); - interpy = R_InterpolateFixed(thing->old_y, thing->y); - interpz = R_InterpolateFixed(thing->old_z, thing->z); + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &tracer_interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &tracer_interp); + } // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) @@ -1871,21 +1855,21 @@ static void R_ProjectSprite(mobj_t *thing) mul = -mul; } - interpx += FixedMul(thing->momx, mul); - interpy += FixedMul(thing->momy, mul); - interpz += FixedMul(thing->momz, mul); + tracer_interp.x += FixedMul(thing->momx, mul); + tracer_interp.y += FixedMul(thing->momy, mul); + tracer_interp.z += FixedMul(thing->momz, mul); } // sprite offset - interpx += thing->sprxoff; - interpy += thing->spryoff; - interpz += thing->sprzoff; + tracer_interp.x += thing->sprxoff; + tracer_interp.y += thing->spryoff; + tracer_interp.z += thing->sprzoff; if (! R_ThingVisible(thing)) return; - tr_x = (interpx + sort_x) - viewx; - tr_y = (interpy + sort_y) - viewy; + tr_x = (tracer_interp.x + sort_x) - viewx; + tr_y = (tracer_interp.y + sort_y) - viewy; tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); linkscale = FixedDiv(projectiony[viewssnum], tz); @@ -1900,8 +1884,8 @@ static void R_ProjectSprite(mobj_t *thing) } else if (splat) { - tr_x = (interpx + sort_x) - viewx; - tr_y = (interpy + sort_y) - viewy; + tr_x = (interp.x + sort_x) - viewx; + tr_y = (interp.y + sort_y) - viewy; sort_z = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); sortscale = FixedDiv(projectiony[viewssnum], sort_z); } @@ -1909,8 +1893,8 @@ static void R_ProjectSprite(mobj_t *thing) // Calculate the splat's sortscale if (splat) { - tr_x = (thing->x - sort_x) - viewx; - tr_y = (thing->y - sort_y) - viewy; + tr_x = (interp.x - sort_x) - viewx; + tr_y = (interp.y - sort_y) - viewy; sort_z = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); sortsplat = FixedDiv(projectiony[viewssnum], sort_z); } @@ -1921,7 +1905,7 @@ static void R_ProjectSprite(mobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; - if (P_PointOnLineSide(interpx, interpy, portalclipline) != 0) + if (P_PointOnLineSide(interp.x, interp.y, portalclipline) != 0) return; } @@ -1949,7 +1933,7 @@ static void R_ProjectSprite(mobj_t *thing) if (shadowdraw || shadoweffects) { - fixed_t groundz = R_GetShadowZ(thing, NULL, interpx, interpy, interpz); + fixed_t groundz = R_GetShadowZ(thing, NULL); boolean isflipped = (thing->eflags & MFE_VERTICALFLIP); if (shadoweffects) @@ -1973,9 +1957,9 @@ static void R_ProjectSprite(mobj_t *thing) if (shadowskew) { - R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan, interpx, interpy); + R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan); - gzt = (isflipped ? (thing->z + thing->height) : thing->z) + patch->height * spriteyscale / 2; + gzt = (isflipped ? (interp.z + thing->height) : interp.z) + patch->height * spriteyscale / 2; gz = gzt - patch->height * spriteyscale; cut |= SC_SHEAR; @@ -1990,12 +1974,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 = interpz + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); + gz = interp.z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale)); } else { - gzt = interpz + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); + gzt = interp.z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); gz = gzt - FixedMul(spr_height, FixedMul(spriteyscale, this_scale)); } } @@ -2014,7 +1998,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], interpx, interpy); + fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interp.x, interp.y); if (h <= top) { light = lightnum - 1; break; @@ -2040,12 +2024,12 @@ static void R_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { if (viewz < sectors[phs].floorheight ? - interpz >= sectors[heightsec].floorheight : + interp.z >= sectors[heightsec].floorheight : gzt < sectors[heightsec].floorheight) return; if (viewz > sectors[phs].ceilingheight ? gzt < sectors[heightsec].ceilingheight && viewz >= sectors[heightsec].ceilingheight : - interpz >= sectors[heightsec].ceilingheight) + interp.z >= sectors[heightsec].ceilingheight) return; } @@ -2058,12 +2042,12 @@ static void R_ProjectSprite(mobj_t *thing) vis->sortscale = sortscale; vis->sortsplat = sortsplat; vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15 - vis->gx = interpx; - vis->gy = interpy; + vis->gx = interp.x; + vis->gy = interp.y; vis->gz = gz; vis->gzt = gzt; vis->thingheight = thing->height; - vis->pz = interpz; + vis->pz = interp.z; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = FixedDiv(gzt - viewz, spriteyscale); vis->scalestep = scalestep; @@ -2094,7 +2078,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000 vis->scale = FixedMul(spriteyscale, yscale); //<thingscale = oldthing->scale; + vis->thingscale = interp.scale; vis->spritexscale = spritexscale; vis->spriteyscale = spriteyscale; @@ -2176,8 +2160,7 @@ static void R_ProjectSprite(mobj_t *thing) if (oldthing->shadowscale && cv_shadow.value) { - R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz, - interpx, interpy, interpz); + R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz); } // Debug @@ -2204,13 +2187,21 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) fixed_t gz, gzt; // uncapped/interpolation - fixed_t interpx = R_InterpolateFixed(thing->old_x, thing->x); - fixed_t interpy = R_InterpolateFixed(thing->old_y, thing->y); - fixed_t interpz = R_InterpolateFixed(thing->old_z, thing->z); + interpmobjstate_t interp = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolatePrecipMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolatePrecipMobjState(thing, FRACUNIT, &interp); + } // transform the origin point - tr_x = interpx - viewx; - tr_y = interpy - viewy; + tr_x = interp.x - viewx; + tr_y = interp.y - viewy; tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance @@ -2274,12 +2265,12 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; - if (P_PointOnLineSide(interpx, interpy, portalclipline) != 0) + if (P_PointOnLineSide(interp.x, interp.y, portalclipline) != 0) return; } //SoM: 3/17/2000: Disregard sprites that are out of view.. - gzt = interpz + spritecachedinfo[lump].topoffset; + gzt = interp.z + spritecachedinfo[lump].topoffset; gz = gzt - spritecachedinfo[lump].height; if (thing->subsector->sector->cullheight) @@ -2292,12 +2283,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 = interpx; - vis->gy = interpy; + vis->gx = interp.x; + vis->gy = interp.y; vis->gz = gz; vis->gzt = gzt; vis->thingheight = 4*FRACUNIT; - vis->pz = interpz; + vis->pz = interp.z; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = 0; diff --git a/src/r_things.h b/src/r_things.h index 98f9b2618..db695cf3c 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -59,7 +59,7 @@ void R_DrawFlippedMaskedColumn(column_t *column, column_t *brightmap); extern INT16 negonearray[MAXVIDWIDTH]; extern INT16 screenheightarray[MAXVIDWIDTH]; -fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope, fixed_t interpx, fixed_t interpy, fixed_t interpz); +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope); //SoM: 6/5/2000: Light sprites correctly! void R_AddSprites(sector_t *sec, INT32 lightlevel); diff --git a/src/screen.c b/src/screen.c index 43e8ddc2d..58d3fde47 100644 --- a/src/screen.c +++ b/src/screen.c @@ -15,6 +15,7 @@ #include "screen.h" #include "console.h" #include "am_map.h" +#include "i_time.h" #include "i_system.h" #include "i_video.h" #include "r_local.h" @@ -549,12 +550,12 @@ void SCR_CalculateFPS(void) return; } - updateElapsed = I_PreciseToMicros(endTime - updateTime); + updateElapsed = (endTime - updateTime) / (I_GetPrecisePrecision() / 1000000); if (updateElapsed >= FPS_SAMPLE_RATE) { static int sampleIndex = 0; - int frameElapsed = I_PreciseToMicros(endTime - startTime); + int frameElapsed = (endTime - startTime) / (I_GetPrecisePrecision() / 1000000); fps_samples[sampleIndex] = frameElapsed / 1000.0f; @@ -581,7 +582,7 @@ void SCR_DisplayTicRate(void) UINT32 cap = R_GetFramerateCap(); UINT32 benchmark = (cap == 0) ? I_GetRefreshRate() : cap; INT32 x = 318; - double fps = ceil(averageFPS); + double fps = round(averageFPS); // draw "FPS" V_DrawFixedPatch(306< 0) - SDL_Delay(cv_sleep.value); -} - -boolean I_CheckFrameCap(precise_t start, precise_t end) -{ - UINT32 capFrames = R_GetFramerateCap(); - int capMicros = 0; - - int elapsed; - - if (capFrames == 0) - { - // We don't want to cap. - return false; - } - - elapsed = I_PreciseToMicros(end - start); - capMicros = 1000000 / capFrames; - - if (elapsed < capMicros) - { - // Wait to draw the next frame. - UINT32 wait = ((capMicros - elapsed) / 1000); - - if (cv_sleep.value > 1) - { - // 1 is the default, and in non-interpolated mode is just the bare minimum wait. - // Since we're already adding some wait with an FPS cap, only apply when it's above 1. - wait += cv_sleep.value - 1; - } - - // If the wait's greater than our granularity value, - // we'll just burn the couple extra cycles in the main loop - // in order to get to the next frame. - // This makes us get to the exact FPS cap more often. - - // Higher values have more wasted CPU cycles, but the in-game frame performance is better. - // 10ms is the average clock tick of most OS scheduling. - // 15ms is a little more than that, for leniency on slow machines. (This helps mine reach a stable 60, at least!) - // (https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdldelay.html) -#define DELAY_GRANULARITY 15 - if (wait >= DELAY_GRANULARITY) - { - SDL_Delay(wait); - } -#undef DELAY_GRANULARITY - - return true; - } - - // Waited enough to draw again. - return false; + SDL_Delay(ms); } #ifdef NEWSIGNALHANDLER diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 9f96a2177..fd7a93707 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1293,7 +1293,7 @@ void I_FinishUpdate(void) if (rendermode == render_none) return; //Alam: No software or OpenGl surface - //SCR_CalculateFPS(); // Moved to main loop + SCR_CalculateFPS(); if (I_SkipFrame()) return; @@ -1710,6 +1710,27 @@ boolean VID_CheckRenderer(void) return rendererchanged; } +static UINT32 refresh_rate; +static UINT32 VID_GetRefreshRate(void) +{ + int index = SDL_GetWindowDisplayIndex(window); + SDL_DisplayMode m; + + if (SDL_WasInit(SDL_INIT_VIDEO) == 0) + { + // Video not init yet. + return 0; + } + + if (SDL_GetCurrentDisplayMode(index, &m) != 0) + { + // Error has occurred. + return 0; + } + + return m.refresh_rate; +} + INT32 VID_SetMode(INT32 modeNum) { SDLdoUngrabMouse(); @@ -1729,6 +1750,8 @@ INT32 VID_SetMode(INT32 modeNum) src_rect.w = vid.width; src_rect.h = vid.height; + refresh_rate = VID_GetRefreshRate(); + //Impl_SetWindowName("SRB2Kart "VERSIONSTRING); VID_CheckRenderer(); return SDL_TRUE; @@ -2074,20 +2097,10 @@ void I_ShutdownGraphics(void) UINT32 I_GetRefreshRate(void) { - int index = SDL_GetWindowDisplayIndex(window); - SDL_DisplayMode m; - - if (SDL_WasInit(SDL_INIT_VIDEO) == 0) - { - // Video not init yet. - return 0; - } - - if (SDL_GetDesktopDisplayMode(index, &m) != 0) - { - // Error has occurred. - return 0; - } - - return m.refresh_rate; + // Moved to VID_GetRefreshRate. + // Precalculating it like that won't work as + // well for windowed mode since you can drag + // the window around, but very slow PCs might have + // trouble querying mode over and over again. + return refresh_rate; } diff --git a/src/st_stuff.c b/src/st_stuff.c index f1af9465c..fbfbedb9b 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -44,6 +44,7 @@ #include "hardware/hw_main.h" #endif +#include "lua_hudlib_drawlist.h" #include "lua_hud.h" // SRB2Kart @@ -51,6 +52,8 @@ #include "v_video.h" #include "r_skins.h" // NUMFACES +#include "r_fps.h" + UINT16 objectsdrawn = 0; // @@ -175,6 +178,9 @@ hudinfo_t hudinfo[NUMHUDITEMS] = { 288, 176, V_SNAPTORIGHT|V_SNAPTOBOTTOM}, // HUD_POWERUPS }; +static huddrawlist_h luahuddrawlist_game; +static huddrawlist_h luahuddrawlist_titlecard; + // // STATUS BAR CODE // @@ -448,6 +454,9 @@ void ST_Init(void) return; ST_LoadGraphics(); + + luahuddrawlist_game = LUA_HUD_CreateDrawList(); + luahuddrawlist_titlecard = LUA_HUD_CreateDrawList(); } // change the status bar too, when pressing F12 while viewing a demo. @@ -1166,8 +1175,12 @@ void ST_drawTitleCard(void) lt_lasttic = lt_ticker; luahook: - LUAh_TitleCardHUD(stplyr); - + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_titlecard); + LUAh_TitleCardHUD(stplyr, luahuddrawlist_titlecard); + } + LUA_HUD_DrawList(luahuddrawlist_titlecard); } // Clear defined coordinates, we don't need them anymore @@ -1236,7 +1249,14 @@ static void ST_overlayDrawer(void) } if (!(netgame || multiplayer) || !hu_showscores) - LUAh_GameHUD(stplyr); + { + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_game); + LUAh_GameHUD(stplyr, luahuddrawlist_game); + } + LUA_HUD_DrawList(luahuddrawlist_game); + } if (!hu_showscores && netgame && !mapreset) { diff --git a/src/w_wad.c b/src/w_wad.c index df8df7928..f2c155305 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -60,6 +60,7 @@ #include "r_textures.h" #include "r_patch.h" #include "r_picformats.h" +#include "i_time.h" #include "i_system.h" #include "md5.h" #include "lua_script.h" diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index 9b2c5bd07..c8abf005a 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -35,6 +35,7 @@ #include #include "../m_misc.h" +#include "../i_time.h" #include "../i_video.h" #include "../i_sound.h" #include "../i_system.h" @@ -262,15 +263,25 @@ tic_t I_GetTime(void) return newtics; } -fixed_t I_GetTimeFrac(void) +precise_t I_GetPreciseTime(void) { - return 0; + LARGE_INTEGER time; + BOOL res = QueryPerformanceCounter(&time); + if (!res) I_Error("QueryPerformanceCounter error"); // if this happens, you've gone back to the 90s + return (precise_t) time.QuadPart; } -void I_Sleep(void) +UINT64 I_GetPrecisePrecision(void) { - if (cv_sleep.value > 0) - Sleep(cv_sleep.value); + LARGE_INTEGER time; + BOOL res = QueryPerformanceFrequency(&time); + if (!res) I_Error("QueryPerformanceFrequency error"); // if this happens, you've gone back to the 90s + return (precise_t) time.QuadPart; +} + +void I_Sleep(UINT32 ms) +{ + Sleep(ms); } // should move to i_video diff --git a/src/y_inter.c b/src/y_inter.c index a1a414d19..a5f485a12 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -38,6 +38,7 @@ #include "lua_hook.h" // IntermissionThinker hook #include "lua_hud.h" +#include "lua_hudlib_drawlist.h" #include "m_random.h" // M_RandomKey #include "g_input.h" // PlayerInputDown @@ -113,7 +114,10 @@ static INT32 sorttic = -1; intertype_t intertype = int_none; intertype_t intermissiontypes[NUMGAMETYPES]; +static huddrawlist_h luahuddrawlist_intermission; + static void Y_FollowIntermission(void); + static void Y_RescaleScreenBuffer(void); static void Y_UnloadData(void); @@ -472,7 +476,13 @@ void Y_IntermissionDrawer(void) else if (bgtile) V_DrawPatchFill(bgtile); - LUAh_IntermissionHUD(); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_intermission); + LUAh_IntermissionHUD(luahuddrawlist_intermission); + } + LUA_HUD_DrawList(luahuddrawlist_intermission); + if (!LUA_HudEnabled(hud_intermissiontally)) goto skiptallydrawer; @@ -1133,6 +1143,9 @@ void Y_StartIntermission(void) break; } + LUA_HUD_DestroyDrawList(luahuddrawlist_intermission); + luahuddrawlist_intermission = LUA_HUD_CreateDrawList(); + if (powertype != PWRLV_DISABLED) { K_UpdatePowerLevels();