diff --git a/src/Makefile.d/versions.mk b/src/Makefile.d/versions.mk index f0b59658ee741e7d709b4d4037b1745a1e3bfefb..d2877b374346115621773c822af00a92dd4de72c 100644 --- a/src/Makefile.d/versions.mk +++ b/src/Makefile.d/versions.mk @@ -156,7 +156,7 @@ ifdef DEBUGMODE ifdef GCC48 opts+=-Og else -opts+=O0 +opts+=-O0 endif endif diff --git a/src/Sourcefile b/src/Sourcefile index 983dadaf0cbea42079ce68f032323c6c03a4a595..de90bb60910286242ae4b62b41af5d9ad57706ad 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -18,6 +18,7 @@ am_map.c command.c console.c hu_stuff.c +i_time.c y_inter.c st_stuff.c m_aatree.c @@ -55,6 +56,7 @@ tables.c r_bsp.c r_data.c r_draw.c +r_fps.c r_main.c r_plane.c r_segs.c @@ -95,4 +97,5 @@ lua_taglib.c lua_polyobjlib.c lua_blockmaplib.c lua_hudlib.c +lua_hudlib_drawlist.c lua_inputlib.c diff --git a/src/android/i_system.c b/src/android/i_system.c index 752e9f6c6bb4138caaf17004cbcc0fcaa6b66cee..1fba4d82536bc532cd8ee498afd105c35fd59d15 100644 --- a/src/android/i_system.c +++ b/src/android/i_system.c @@ -82,13 +82,17 @@ 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; } -void I_Sleep(void){} +UINT64 I_GetPrecisePrecision(void) +{ + return 1000000; +} void I_GetEvent(void){} diff --git a/src/b_bot.c b/src/b_bot.c index 775a13e294cf31e8070a9f6fc51894f91317496c..f1df5abbd445ee14a62135ddfe65934a35432a66 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -579,7 +579,7 @@ void B_RespawnBot(INT32 playernum) player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR); - P_TeleportMove(tails, x, y, z); + P_SetOrigin(tails, x, y, z); if (player->charability == CA_FLY) { P_SetPlayerMobjState(tails, S_PLAY_FLY); diff --git a/src/command.c b/src/command.c index dae4dc7b160e1399ccba0ae6f4874cf120e0524c..c849341ffcc02b3fa8ca0435c5640439b0fd0aa8 100644 --- a/src/command.c +++ b/src/command.c @@ -2076,9 +2076,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 ac8bba608288063b7b34dff533f9d2ef0e9c84e2..4cd6333c5e6f1b8a831041d36927b0ee82bc4a1d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -15,6 +15,7 @@ #include <unistd.h> //for unlink #endif +#include "i_time.h" #include "i_net.h" #include "i_system.h" #include "i_video.h" @@ -113,6 +114,9 @@ static INT16 consistancy[BACKUPTICS]; static UINT8 player_joining = false; UINT8 hu_redownloadinggamestate = 0; +// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks +boolean hu_stopped = false; + UINT8 adminpassmd5[16]; boolean adminpasswordset = false; @@ -2441,7 +2445,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; } @@ -5206,8 +5213,10 @@ static void SV_Maketic(void) maketic++; } -void TryRunTics(tic_t realtics) +boolean TryRunTics(tic_t realtics) { + boolean ticking; + // the machine has lagged but it is not so bad if (realtics > TICRATE/7) // FIXME: consistency failure!! { @@ -5231,7 +5240,7 @@ void TryRunTics(tic_t realtics) if (demoplayback) { - neededtic = gametic + (realtics * cv_playbackspeed.value); + neededtic = gametic + realtics; // start a game after a demo maketic += realtics; firstticstosend = maketic; @@ -5251,10 +5260,22 @@ void TryRunTics(tic_t realtics) } #endif + ticking = neededtic > gametic; + + if (ticking) + { + if (realtics) + hu_stopped = false; + } + if (player_joining) - return; + { + if (realtics) + hu_stopped = true; + return false; + } - if (neededtic > gametic) + if (ticking) { if (advancedemo) { @@ -5290,6 +5311,13 @@ void TryRunTics(tic_t realtics) break; } } + else + { + if (realtics) + hu_stopped = true; + } + + return ticking; } /* diff --git a/src/d_clisrv.h b/src/d_clisrv.h index bf3f0b64f5126eca92510a07686e10afbd07cba7..e07864122455bf0960e2299ba5ef7c3d9b2ac39f 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -430,7 +430,7 @@ boolean Playing(void); void D_QuitNetGame(void); //? How many ticks to run? -void TryRunTics(tic_t realtic); +boolean TryRunTics(tic_t realtic); // extra data for lmps // these functions scare me. they contain magic. @@ -458,4 +458,7 @@ extern UINT8 hu_redownloadinggamestate; extern UINT8 adminpassmd5[16]; extern boolean adminpasswordset; + +extern boolean hu_stopped; + #endif diff --git a/src/d_main.c b/src/d_main.c index fa9e21337ced42286e27b75a7bd2987ee62538e5..91a42ff140dd60277b5b70edb1f82587753354cd 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" @@ -64,6 +65,7 @@ #include "deh_tables.h" // Dehacked list test #include "m_cond.h" // condition initialization #include "fastcmp.h" +#include "r_fps.h" // Frame interpolation/uncapped #include "keys.h" #include "filesrch.h" // refreshdirmenu #include "g_input.h" // tutorial mode control scheming @@ -476,6 +478,7 @@ static void D_Display(void) if (!automapactive && !dedicated && cv_renderview.value) { + R_ApplyLevelInterpolators(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); PS_START_TIMING(ps_rendercalltime); if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) { @@ -524,6 +527,7 @@ static void D_Display(void) V_DoPostProcessor(1, postimgtype2, postimgparam2); } PS_STOP_TIMING(ps_rendercalltime); + R_RestoreLevelInterpolators(); } if (lastdraw) @@ -692,9 +696,14 @@ tic_t rendergametic; void D_SRB2Loop(void) { - tic_t oldentertics = 0, entertic = 0, realtics = 0, rendertimeout = INFTICS; + tic_t entertic = 0, oldentertics = 0, realtics = 0, rendertimeout = INFTICS; + double deltatics = 0.0; + double deltasecs = 0.0; static lumpnum_t gstartuplumpnum; + boolean interp = false; + boolean doDisplay = false; + if (dedicated) server = true; @@ -705,6 +714,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() @@ -745,6 +755,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; @@ -756,7 +779,11 @@ void D_SRB2Loop(void) realtics = entertic - oldentertics; oldentertics = entertic; - refreshdirmenu = 0; // not sure where to put this, here as good as any? + if (demoplayback && gamestate == GS_LEVEL) + { + // Nicer place to put this. + realtics = realtics * cv_playbackspeed.value; + } #ifdef DEBUGFILE if (!realtics) @@ -764,64 +791,119 @@ void D_SRB2Loop(void) debugload--; #endif - if (!realtics && !singletics) - { - I_Sleep(); - continue; - } + interp = R_UsingFrameInterpolation() && !dedicated; + doDisplay = false; #ifdef HW3SOUND HW3S_BeginFrameUpdate(); #endif - // don't skip more than 10 frames at a time - // (fadein / fadeout cause massive frame skip!) - if (realtics > 8) - realtics = 1; - - // process tics (but maybe not if realtic == 0) - TryRunTics(realtics); + refreshdirmenu = 0; // not sure where to put this, here as good as any? - if (lastdraw || singletics || gametic > rendergametic) + if (realtics > 0 || singletics) { - rendergametic = gametic; - rendertimeout = entertic+TICRATE/17; + // don't skip more than 10 frames at a time + // (fadein / fadeout cause massive frame skip!) + if (realtics > 8) + realtics = 1; - // Update display, next frame, with current state. - D_Display(); + // process tics (but maybe not if realtic == 0) + TryRunTics(realtics); + + if (lastdraw || singletics || gametic > rendergametic) + { + rendergametic = gametic; + rendertimeout = entertic + TICRATE/17; + + doDisplay = true; + } + else if (rendertimeout < entertic) // in case the server hang or netsplit + { + // Lagless camera! Yay! + if (gamestate == GS_LEVEL && netgame) + { + // Evaluate the chase cam once for every local realtic + // This might actually be better suited inside G_Ticker or TryRunTics + for (tic_t chasecamtics = 0; chasecamtics < realtics; chasecamtics++) + { + if (splitscreen && camera2.chase) + P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false); + if (camera.chase) + P_MoveChaseCamera(&players[displayplayer], &camera, false); + } + R_UpdateViewInterpolation(); + } - if (moviemode) - M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. - M_DoScreenShot(); + doDisplay = true; + } + + renderisnewtic = true; + } + else + { + renderisnewtic = false; } - else if (rendertimeout < entertic) // in case the server hang or netsplit + + if (interp) { - // Lagless camera! Yay! - if (gamestate == GS_LEVEL && netgame) + // I looked at the possibility of putting in a float drawer for + // perfstats and it's very complicated, so we'll just do this instead... + ps_interp_frac.value.p = (precise_t)((FIXED_TO_FLOAT(g_time.timefrac)) * 1000.0f); + ps_interp_lag.value.p = (precise_t)((deltasecs) * 1000.0f); + + renderdeltatics = FLOAT_TO_FIXED(deltatics); + + if (!(paused || P_AutoPause()) && deltatics < 1.0 && !hu_stopped) { - if (splitscreen && camera2.chase) - P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false); - if (camera.chase) - P_MoveChaseCamera(&players[displayplayer], &camera, false); + rendertimefrac = g_time.timefrac; } - D_Display(); + else + { + rendertimefrac = FRACUNIT; + } + } + else + { + renderdeltatics = realtics * FRACUNIT; + rendertimefrac = FRACUNIT; + } - if (moviemode) - M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. - M_DoScreenShot(); + if (interp || doDisplay) + { + D_Display(); } - // consoleplayer -> displayplayer (hear sounds from viewpoint) + // Only take screenshots after drawing. + if (moviemode) + M_SaveFrame(); + if (takescreenshot) + M_DoScreenShot(); + + // consoleplayer -> displayplayers (hear sounds from viewpoint) S_UpdateSounds(); // move positional sounds - S_UpdateClosedCaptions(); + if (realtics > 0 || singletics) + S_UpdateClosedCaptions(); #ifdef HW3SOUND HW3S_EndFrameUpdate(); #endif LUA_Step(); + + // 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; } } @@ -1307,8 +1389,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(); // Make backups of some SOCcable tables. P_BackupTables(); @@ -1583,6 +1665,8 @@ void D_SRB2Main(void) // as having been modified for the first game. M_PushSpecialParameters(); // push all "+" parameter at the command buffer + COM_BufExecute(); // ensure the command buffer gets executed before the map starts (+skin) + if (M_CheckParm("-gametype") && M_IsNextParm()) { // from Command_Map_f diff --git a/src/d_net.c b/src/d_net.c index 5e5c10889c2a3f2a0383f005107d0ce277e7082c..a7e1eb16d6d4fc53f118da9e45d8f8baf4f94590 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" @@ -614,7 +615,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 1733d33240753f269a7eabd1e86de449d3e81597..4e90db0dcc81c3bf45b03d8951f5465bdd2cabf6 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" @@ -191,7 +192,7 @@ static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}}; static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; -static CV_PossibleValue_t sleeping_cons_t[] = {{-1, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t sleeping_cons_t[] = {{0, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}}; static CV_PossibleValue_t competitionboxes_cons_t[] = {{0, "Normal"}, {1, "Mystery"}, //{2, "Teleport"}, {3, "None"}, {0, NULL}}; diff --git a/src/d_netfil.c b/src/d_netfil.c index 37fb7265f8abe714d90dab7c0882f55d73bc1cb0..edbef30bbf92ba6156da44f3d30fb32d8ab0cccb 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -35,6 +35,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 6df6689c5fcf4f1085511218d7f19047cd8a7c0f..1067c86814e1398c26f67301a8d9680d773a6fe9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -383,6 +383,8 @@ typedef struct player_s // fun thing for player sprite angle_t drawangle; + angle_t old_drawangle; + angle_t old_drawangle2; // player's ring count INT16 rings; diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c index a1d7572048d2f65144150530f6aa3dc52d9ae7ea..997115ad0c8da17a2aa66e727ab166b500a6dee4 100644 --- a/src/dummy/i_system.c +++ b/src/dummy/i_system.c @@ -14,24 +14,16 @@ UINT32 I_GetFreeMem(UINT32 *total) return 0; } -tic_t I_GetTime(void) -{ - return 0; -} +void I_Sleep(UINT32 ms){} -precise_t I_GetPreciseTime(void) -{ +precise_t I_GetPreciseTime(void) { return 0; } -int I_PreciseToMicros(precise_t d) -{ - (void)d; - 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 ec325206b426a4ffb986232915f5b3abc06172ce..ef5b5cbf49d546df245f0febdbd2a6711830c058 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" @@ -62,8 +63,6 @@ static tic_t stoptimer; static boolean keypressed = false; // (no longer) De-Demo'd Title Screen -static tic_t xscrolltimer; -static tic_t yscrolltimer; static INT32 menuanimtimer; // Title screen: background animation timing mobj_t *titlemapcameraref = NULL; @@ -229,6 +228,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. @@ -517,9 +518,9 @@ void F_StartIntro(void) } // -// F_IntroDrawScene +// F_IntroDrawer // -static void F_IntroDrawScene(void) +void F_IntroDrawer(void) { boolean highres = true; INT32 cx = 8, cy = 128; @@ -625,24 +626,22 @@ static void F_IntroDrawScene(void) if (intro_curtime > 1 && intro_curtime < (INT32)introscenetime[intro_scenenum]) { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + if (intro_curtime < TICRATE-5) // Make the text shine! + { sprintf(stjrintro, "STJRI%03u", intro_curtime-1); + } else if (intro_curtime >= TICRATE-6 && intro_curtime < 2*TICRATE-20) // Pause on black screen for just a second + { return; + } else if (intro_curtime == 2*TICRATE-19) { // Fade in the text // The text fade out is automatically handled when switching to a new intro scene strncpy(stjrintro, "STJRI029", 9); - S_ChangeMusicInternal("_stjr", false); - background = W_CachePatchName(stjrintro, PU_PATCH_LOWPRIORITY); - wipestyleflags = WSF_FADEIN; - F_WipeStartScreen(); - F_TryColormapFade(31); V_DrawSmallScaledPatch(bgxoffs, 84, 0, background); - F_WipeEndScreen(); - F_RunWipe(0,true); } if (!WipeInAction) // Draw the patch if not in a wipe @@ -841,17 +840,27 @@ static void F_IntroDrawScene(void) V_DrawRightAlignedString(BASEVIDWIDTH-4, BASEVIDHEIGHT-12, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), "\x86""Press ""\x82""ENTER""\x86"" to skip..."); } - if (animtimer) - animtimer--; - V_DrawString(cx, cy, V_ALLOWLOWERCASE, cutscene_disptext); } // -// F_IntroDrawer +// F_IntroTicker // -void F_IntroDrawer(void) +void F_IntroTicker(void) { + // advance animation + finalecount++; + + timetonext--; + + F_WriteText(); + + // check for skipping + if (keypressed) + keypressed = false; + + wipestyleflags = WSF_CROSSFADE; + if (timetonext <= 0) { if (intro_scenenum == 0) @@ -861,6 +870,9 @@ void F_IntroDrawer(void) wipestyleflags = WSF_FADEOUT; F_WipeStartScreen(); F_TryColormapFade(31); + + F_IntroDrawer(); + F_WipeEndScreen(); F_RunWipe(99,true); } @@ -874,6 +886,9 @@ void F_IntroDrawer(void) wipestyleflags = (WSF_FADEOUT|WSF_TOWHITE); F_WipeStartScreen(); F_TryColormapFade(0); + + F_IntroDrawer(); + F_WipeEndScreen(); F_RunWipe(99,true); } @@ -885,6 +900,9 @@ void F_IntroDrawer(void) wipestyleflags = WSF_FADEOUT; F_WipeStartScreen(); F_TryColormapFade(31); + + F_IntroDrawer(); + F_WipeEndScreen(); F_RunWipe(99,true); } @@ -897,7 +915,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(); @@ -920,12 +941,12 @@ void F_IntroDrawer(void) wipegamestate = GS_INTRO; return; } + F_NewCutscene(introtext[++intro_scenenum]); timetonext = introscenetime[intro_scenenum]; F_WipeStartScreen(); wipegamestate = -1; - wipestyleflags = WSF_CROSSFADE; animtimer = stoptimer = 0; } @@ -933,78 +954,36 @@ void F_IntroDrawer(void) if (rendermode != render_none) { - if (intro_scenenum == 5 && intro_curtime == 5*TICRATE) + if (intro_scenenum == 0 && intro_curtime == 2*TICRATE-19) { - patch_t *radar = W_CachePatchName("RADAR", PU_PATCH_LOWPRIORITY); + S_ChangeMusicInternal("_stjr", false); + wipestyleflags = WSF_FADEIN; F_WipeStartScreen(); - F_WipeColorFill(31); - V_DrawSmallScaledPatch(0, 0, 0, radar); - W_UnlockCachedPatch(radar); - V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); + F_TryColormapFade(31); - F_WipeEndScreen(); - F_RunWipe(99,true); - } - else if (intro_scenenum == 7 && intro_curtime == 6*TICRATE) // Force a wipe here - { - patch_t *grass = W_CachePatchName("SGRASS2", PU_PATCH_LOWPRIORITY); - - F_WipeStartScreen(); - F_WipeColorFill(31); - V_DrawSmallScaledPatch(0, 0, 0, grass); - W_UnlockCachedPatch(grass); - V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); + F_IntroDrawer(); F_WipeEndScreen(); F_RunWipe(99,true); } - /*else if (intro_scenenum == 11 && intro_curtime == 7*TICRATE) + else if ((intro_scenenum == 5 && intro_curtime == 5*TICRATE) + || (intro_scenenum == 7 && intro_curtime == 6*TICRATE) + //|| (intro_scenenum == 11 && intro_curtime == 7*TICRATE) + || (intro_scenenum == 15 && intro_curtime == 7*TICRATE)) { - patch_t *confront = W_CachePatchName("CONFRONT", PU_PATCH_LOWPRIORITY); - F_WipeStartScreen(); F_WipeColorFill(31); - V_DrawSmallScaledPatch(0, 0, 0, confront); - W_UnlockCachedPatch(confront); - V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); - - F_WipeEndScreen(); - F_RunWipe(99,true); - }*/ - if (intro_scenenum == 15 && intro_curtime == 7*TICRATE) - { - patch_t *sdo = W_CachePatchName("SONICDO2", PU_PATCH_LOWPRIORITY); - F_WipeStartScreen(); - F_WipeColorFill(31); - V_DrawSmallScaledPatch(0, 0, 0, sdo); - W_UnlockCachedPatch(sdo); - V_DrawString(224, 8, V_ALLOWLOWERCASE, cutscene_disptext); + F_IntroDrawer(); F_WipeEndScreen(); F_RunWipe(99,true); } } - F_IntroDrawScene(); -} - -// -// F_IntroTicker -// -void F_IntroTicker(void) -{ - // advance animation - finalecount++; - - timetonext--; - - F_WriteText(); - - // check for skipping - if (keypressed) - keypressed = false; + if (animtimer) + animtimer--; } // @@ -2304,6 +2283,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(); } // @@ -2319,6 +2301,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; @@ -2345,12 +2328,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; @@ -3421,7 +3405,21 @@ void F_TitleScreenDrawer(void) } luahook: - LUA_HUDHOOK(title); + // The title drawer is sometimes called without first being started + // In order to avoid use-before-initialization crashes, let's check and + // create the drawlist if it doesn't exist. + if (!LUA_HUD_IsDrawListValid(luahuddrawlist_title)) + { + LUA_HUD_DestroyDrawList(luahuddrawlist_title); + luahuddrawlist_title = LUA_HUD_CreateDrawList(); + } + + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_title); + LUA_HUDHOOK(title, luahuddrawlist_title); + } + LUA_HUD_DrawList(luahuddrawlist_title); } // separate animation timer for backgrounds, since we also count @@ -3858,11 +3856,27 @@ boolean F_ContinueResponder(event_t *event) static INT32 scenenum, cutnum; static INT32 picxpos, picypos, picnum, pictime, picmode, numpics, pictoloop; static INT32 textxpos, textypos; -static boolean dofadenow = false, cutsceneover = false; +static boolean cutsceneover = false; static boolean runningprecutscene = false, precutresetplayer = false; static void F_AdvanceToNextScene(void) { + if (rendermode != render_none) + { + F_WipeStartScreen(); + + // Fade to any palette color you want. + if (cutscenes[cutnum]->scene[scenenum].fadecolor) + { + V_DrawFill(0,0,BASEVIDWIDTH,BASEVIDHEIGHT,cutscenes[cutnum]->scene[scenenum].fadecolor); + + F_WipeEndScreen(); + F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeinid, true); + + F_WipeStartScreen(); + } + } + // Don't increment until after endcutscene check // (possible overflow / bad patch names from the one tic drawn before the fade) if (scenenum+1 >= cutscenes[cutnum]->numscenes) @@ -3870,6 +3884,7 @@ static void F_AdvanceToNextScene(void) F_EndCutScene(); return; } + ++scenenum; timetonext = 0; @@ -3885,7 +3900,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; @@ -3895,6 +3909,14 @@ static void F_AdvanceToNextScene(void) textypos = cutscenes[cutnum]->scene[scenenum].textypos; animtimer = pictime = cutscenes[cutnum]->scene[scenenum].picduration[picnum]; + + if (rendermode != render_none) + { + F_CutsceneDrawer(); + + F_WipeEndScreen(); + F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true); + } } // See also G_AfterIntermission, the only other place which handles intra-map/ending transitions @@ -3969,21 +3991,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset // void F_CutsceneDrawer(void) { - if (dofadenow && rendermode != render_none) - { - F_WipeStartScreen(); - - // Fade to any palette color you want. - if (cutscenes[cutnum]->scene[scenenum].fadecolor) - { - V_DrawFill(0,0,BASEVIDWIDTH,BASEVIDHEIGHT,cutscenes[cutnum]->scene[scenenum].fadecolor); - - F_WipeEndScreen(); - F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeinid, true); - - F_WipeStartScreen(); - } - } V_DrawFill(0,0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); if (cutscenes[cutnum]->scene[scenenum].picname[picnum][0] != '\0') @@ -3996,12 +4003,6 @@ void F_CutsceneDrawer(void) W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_PATCH_LOWPRIORITY)); } - if (dofadenow && rendermode != render_none) - { - F_WipeEndScreen(); - F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true); - } - V_DrawString(textxpos, textypos, V_ALLOWLOWERCASE, cutscene_disptext); } @@ -4018,8 +4019,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 43b7180b754408faf39387ef259d54aae590bf9f..ab869ca605b4afaf7dac023fd45f0aaf4145184b 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -24,6 +24,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" @@ -555,7 +556,10 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu) // wait loop while (!((nowtime = I_GetTime()) - lastwipetic)) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } lastwipetic = nowtime; // Wipe styles diff --git a/src/g_demo.c b/src/g_demo.c index 77c9ab10b2f0ee4295d09c7d81e5e25b5cabc0fa..2da5a76ab2c764d0db0466f023f0cf20007f7c40 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" @@ -1008,7 +1009,7 @@ void G_ReadMetalTic(mobj_t *metal) oldmetal.x = READFIXED(metal_p); oldmetal.y = READFIXED(metal_p); oldmetal.z = READFIXED(metal_p); - P_TeleportMove(metal, oldmetal.x, oldmetal.y, oldmetal.z); + P_MoveOrigin(metal, oldmetal.x, oldmetal.y, oldmetal.z); oldmetal.x = metal->x; oldmetal.y = metal->y; oldmetal.z = metal->z; diff --git a/src/g_game.c b/src/g_game.c index 39d0030565c468823819c0a35d2653d2a773db01..abefac161fefce2f13d28fbc255f2b3500bd275e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -19,6 +19,7 @@ #include "f_finale.h" #include "p_setup.h" #include "p_saveg.h" +#include "i_time.h" #include "i_system.h" #include "am_map.h" #include "m_random.h" @@ -46,6 +47,7 @@ #include "b_bot.h" #include "m_cond.h" // condition sets #include "lua_script.h" +#include "r_fps.h" // frame interpolation/uncapped #include "lua_hud.h" @@ -1903,7 +1905,10 @@ void G_PreLevelTitleCard(void) { // draw loop while (!((nowtime = I_GetTime()) - lasttime)) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } lasttime = nowtime; ST_runTitleCard(); @@ -2362,6 +2367,7 @@ void G_Ticker(boolean run) F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); + break; case GS_INTERMISSION: @@ -2414,7 +2420,9 @@ void G_Ticker(boolean run) break; case GS_TITLESCREEN: - if (titlemapinaction) P_Ticker(run); // then intentionally fall through + if (titlemapinaction) + P_Ticker(run); + // then intentionally fall through /* FALLTHRU */ case GS_WAITINGPLAYERS: F_MenuPresTicker(run); @@ -2801,6 +2809,13 @@ void G_MovePlayerToSpawnOrStarpost(INT32 playernum) P_MovePlayerToStarpost(playernum); else P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum)); + + R_ResetMobjInterpolationState(players[playernum].mo); + + if (playernum == consoleplayer) + P_ResetCamera(&players[playernum], &camera); + else if (playernum == secondarydisplayplayer) + P_ResetCamera(&players[playernum], &camera2); } mapthing_t *G_FindCTFStart(INT32 playernum) diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index 718774773c1dead0dd452617fbb8a5065433fbe7..3b9b8681c26eec0baf19cecafd1cc50fad6d3072 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 scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface); +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface); EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *ptransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 9793a5f690aef4c9f84a2417aadb3581062e9f19..e0d5eeb39f1dbd9f33210d4b6955fa2b369aa192 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" @@ -38,6 +39,7 @@ #include "../m_cheat.h" #include "../f_finale.h" #include "../r_things.h" // R_GetShadowZ +#include "../d_main.h" #include "../p_slopes.h" #include "hw_md2.h" @@ -2968,6 +2970,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) @@ -3025,7 +3028,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); @@ -3104,7 +3122,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) { @@ -3616,6 +3633,18 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) fixed_t slopez; pslope_t *groundslope; + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + groundz = R_GetShadowZ(thing, &groundslope); heightsec = thing->subsector->sector->heightsec; @@ -3636,7 +3665,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) return; } - floordiff = abs((flip < 0 ? thing->height : 0) + thing->z - groundz); + floordiff = abs((flip < 0 ? thing->height : 0) + interp.z - groundz); alpha = floordiff / (4*FRACUNIT) + 75; if (alpha >= 255) return; @@ -3650,8 +3679,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height); fscale = FIXED_TO_FLOAT(scalemul); - fx = FIXED_TO_FLOAT(thing->x); - fy = FIXED_TO_FLOAT(thing->y); + fx = FIXED_TO_FLOAT(interp.x); + fy = FIXED_TO_FLOAT(interp.y); // 3--2 // | /| @@ -3729,12 +3758,44 @@ static void HWR_RotateSpritePolyToAim(gl_vissprite_t *spr, FOutVector *wallVerts && spr && spr->mobj && !R_ThingIsPaperSprite(spr->mobj) && wallVerts) { - float basey = FIXED_TO_FLOAT(spr->mobj->z); - float lowy = wallVerts[0].y; - if (!precip && P_MobjFlip(spr->mobj) == -1) // precip doesn't have eflags so they can't flip + // uncapped/interpolation + interpmobjstate_t interp = {0}; + float basey, lowy; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + if (precip) + { + R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp); + } + } + else + { + if (precip) + { + R_InterpolatePrecipMobjState((precipmobj_t *)spr->mobj, FRACUNIT, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); + } + } + + if (P_MobjFlip(spr->mobj) == -1) + { + basey = FIXED_TO_FLOAT(interp.z + spr->mobj->height); + } + else { - basey = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); + basey = FIXED_TO_FLOAT(interp.z); } + lowy = wallVerts[0].y; + // Rotate sprites to fully billboard with the camera // X, Y, AND Z need to be manipulated for the polys to rotate around the // origin, because of how the origin setting works I believe that should @@ -5013,7 +5074,6 @@ static void HWR_ProjectSprite(mobj_t *thing) INT32 heightsec, phs; const boolean splat = R_ThingIsFloorSprite(thing); const boolean papersprite = (R_ThingIsPaperSprite(thing) && !splat); - angle_t mobjangle = (thing->player ? thing->player->drawangle : thing->angle); float z1, z2; fixed_t spr_width, spr_height; @@ -5023,10 +5083,10 @@ static void HWR_ProjectSprite(mobj_t *thing) INT32 rollangle = 0; #endif - if (!thing) - return; + // uncapped/interpolation + interpmobjstate_t interp = {0}; - if (thing->spritexscale < 1 || thing->spriteyscale < 1) + if (!thing) return; INT32 blendmode; @@ -5044,13 +5104,26 @@ static void HWR_ProjectSprite(mobj_t *thing) dispoffset = thing->info->dispoffset; - this_scale = FIXED_TO_FLOAT(thing->scale); - spritexscale = FIXED_TO_FLOAT(thing->spritexscale); - spriteyscale = FIXED_TO_FLOAT(thing->spriteyscale); + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + 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(thing->x) - gl_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - 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); @@ -5073,8 +5146,8 @@ static void HWR_ProjectSprite(mobj_t *thing) } // The above can stay as it works for cutting sprites that are too close - tr_x = FIXED_TO_FLOAT(thing->x); - tr_y = FIXED_TO_FLOAT(thing->y); + tr_x = FIXED_TO_FLOAT(interp.x); + tr_y = FIXED_TO_FLOAT(interp.y); // decide which patch to use for sprite relative to player #ifdef RANGECHECK @@ -5122,7 +5195,7 @@ static void HWR_ProjectSprite(mobj_t *thing) I_Error("sprframes NULL for sprite %d\n", thing->sprite); #endif - ang = R_PointToAngle (thing->x, thing->y) - mobjangle; + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if (mirrored) ang = InvAngle(ang); @@ -5190,8 +5263,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 { @@ -5200,14 +5273,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((mobjangle)>>ANGLETOFINESHIFT)); - rightcos = FIXED_TO_FLOAT(FINECOSINE((mobjangle)>>ANGLETOFINESHIFT)); + rightsin = FIXED_TO_FLOAT(FINESINE(interp.angle >> ANGLETOFINESHIFT)); + rightcos = FIXED_TO_FLOAT(FINECOSINE(interp.angle >> ANGLETOFINESHIFT)); } else { @@ -5220,14 +5293,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); - fixed_t floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + caster->z - groundz); + 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; @@ -5268,12 +5351,12 @@ static void HWR_ProjectSprite(mobj_t *thing) if (vflip) { - gz = FIXED_TO_FLOAT(thing->z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale); + gz = FIXED_TO_FLOAT(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(thing->z) + (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); } @@ -5292,7 +5375,7 @@ static void HWR_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { float top = gzt; - float bottom = FIXED_TO_FLOAT(thing->z); + float bottom = FIXED_TO_FLOAT(interp.z); if (R_ThingIsFloorSprite(thing)) top = bottom; @@ -5309,13 +5392,24 @@ static void HWR_ProjectSprite(mobj_t *thing) 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); @@ -5438,6 +5532,9 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) unsigned rot = 0; UINT8 flip; + if (!thing) + return; + // Visibility check by the blend mode. if (thing->frame & FF_TRANSMASK) { @@ -5445,9 +5542,22 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) return; } + // 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(thing->x) - gl_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - 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); @@ -5456,8 +5566,8 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) if (tz < ZCLIP_PLANE) return; - tr_x = FIXED_TO_FLOAT(thing->x); - tr_y = FIXED_TO_FLOAT(thing->y); + tr_x = FIXED_TO_FLOAT(interp.x); + tr_y = FIXED_TO_FLOAT(interp.y); // decide which patch to use for sprite relative to player if ((unsigned)thing->sprite >= numsprites) @@ -5520,7 +5630,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) vis->colormap = NULL; // set top/bottom coords - vis->gzt = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset); + vis->gzt = FIXED_TO_FLOAT(interp.z + spritecachedinfo[lumpoff].topoffset); vis->gz = vis->gzt - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height); vis->precip = true; @@ -6656,6 +6766,7 @@ void HWR_DoPostProcessor(player_t *player) // 10 by 10 grid. 2 coordinates (xy) float v[SCREENVERTS][SCREENVERTS][2]; static double disStart = 0; + UINT8 x, y; INT32 WAVELENGTH; INT32 AMPLITUDE; @@ -6664,15 +6775,15 @@ void HWR_DoPostProcessor(player_t *player) // Modifies the wave. if (*type == postimg_water) { - WAVELENGTH = 20; // Lower is longer - AMPLITUDE = 20; // Lower is bigger - FREQUENCY = 16; // Lower is faster + WAVELENGTH = 5; + AMPLITUDE = 20; + FREQUENCY = 8; } else { - WAVELENGTH = 10; // Lower is longer - AMPLITUDE = 30; // Lower is bigger - FREQUENCY = 4; // Lower is faster + WAVELENGTH = 10; + AMPLITUDE = 60; + FREQUENCY = 4; } for (x = 0; x < SCREENVERTS; x++) @@ -6686,7 +6797,7 @@ void HWR_DoPostProcessor(player_t *player) } HWD.pfnPostImgRedraw(v); if (!(paused || P_AutoPause())) - disStart += 1; + disStart += FIXED_TO_FLOAT(renderdeltatics); // Capture the screen again for screen waving on the intermission if(gamestate != GS_INTERMISSION) diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index a003163dbe3b4dc375e6ba0af788d0530a337ad1..f33d67bbbf015e2fb7777c4ad84ec50736e4e7f4 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" @@ -1338,8 +1339,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)); @@ -1349,6 +1350,16 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) angle_t ang; INT32 mod; float finalscale; + interpmobjstate_t interp; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(spr->mobj, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(spr->mobj, FRACUNIT, &interp); + } // Apparently people don't like jump frames like that, so back it goes //if (tics > durs) @@ -1498,8 +1509,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); @@ -1523,7 +1534,11 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } #ifdef USE_MODEL_NEXTFRAME -#define INTERPOLERATION_LIMIT TICRATE/4 + // Interpolate the model interpolation. (lol) + tics -= FixedToFloat(rendertimefrac); + +#define INTERPOLERATION_LIMIT (TICRATE * 0.25f) + if (cv_glmodelinterpolation.value && tics <= durs && tics <= INTERPOLERATION_LIMIT) { if (durs > INTERPOLERATION_LIMIT) @@ -1572,13 +1587,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(spr->mobj->x); - p.y = FIXED_TO_FLOAT(spr->mobj->y)+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(spr->mobj->z); + 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]; @@ -1589,16 +1604,13 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) if (sprframe->rotate || papersprite) { - fixed_t anglef = AngleFixed(spr->mobj->angle); - - if (spr->mobj->player) - anglef = AngleFixed(spr->mobj->player->drawangle); + fixed_t anglef = AngleFixed(interp.angle); p.angley = FIXED_TO_FLOAT(anglef); } else { - const fixed_t anglef = AngleFixed((R_PointToAngle(spr->mobj->x, spr->mobj->y))-ANGLE_180); + const fixed_t anglef = AngleFixed((R_PointToAngle(interp.x, interp.y))-ANGLE_180); p.angley = FIXED_TO_FLOAT(anglef); } @@ -1620,7 +1632,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) p.rotaxis = (UINT8)(sprinfo->pivot[(spr->mobj->frame & FF_FRAMEMASK)].rotaxis); // for NiGHTS specifically but should work everywhere else - ang = R_PointToAngle (spr->mobj->x, spr->mobj->y) - (spr->mobj->player ? spr->mobj->player->drawangle : spr->mobj->angle); + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right p.rollflip = 1; else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 7ec7ee2702f4bdaadbef776a0b281b7c3506f6a9..a58097c91a878f96ecc45b65397c3208ad52add2 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2672,7 +2672,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 scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) { static GLRGBAFloat poly = {0,0,0,0}; static GLRGBAFloat tint = {0,0,0,0}; @@ -2701,11 +2701,11 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 scaley = scale; scalez = scale; - 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; @@ -2977,7 +2977,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 scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) { DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface); } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5d893a5515fcf21e82f72959b9aa0cc8773ad96c..b02eecb6c0d12fdd0d697cbedf76a530d5ab9810 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -48,6 +48,7 @@ #endif #include "lua_hud.h" +#include "lua_hudlib_drawlist.h" #include "lua_hook.h" // coords are scaled @@ -166,10 +167,14 @@ static tic_t cechotimer = 0; static tic_t cechoduration = 5*TICRATE; static INT32 cechoflags = 0; +static huddrawlist_h luahuddrawlist_scores; + //====================================================================== // HEADS UP INIT //====================================================================== +static tic_t resynch_ticker = 0; + #ifndef NONET // just after static void Command_Say_f(void); @@ -332,6 +337,8 @@ void HU_Init(void) // set shift translation table shiftxform = english_shiftxform; + + luahuddrawlist_scores = LUA_HUD_CreateDrawList(); } static inline void HU_Stop(void) @@ -382,12 +389,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<chat_nummsg_min-1;i++) { - strcpy(chat_mini[i], chat_mini[i+1]); - chat_timers[i] = chat_timers[i+1]; - } + for(i=0;i<chat_nummsg_min-1;i++) { + strcpy(chat_mini[i], chat_mini[i+1]); + chat_timers[i] = chat_timers[i+1]; + } chat_nummsg_min--; // lost 1 msg. // use addy and make shit slide smoothly af. @@ -400,10 +407,10 @@ static void HU_removeChatText_Log(void) { // MPC: Don't create new arrays, just iterate through an existing one size_t i; - for(i=0;i<chat_nummsg_log-1;i++) { - strcpy(chat_log[i], chat_log[i+1]); - } - chat_nummsg_log--; // lost 1 msg. + for(i=0;i<chat_nummsg_log-1;i++) { + strcpy(chat_log[i], chat_log[i+1]); + } + chat_nummsg_log--; // lost 1 msg. } #endif @@ -874,6 +881,39 @@ void HU_Ticker(void) hu_showscores = !chat_on; else hu_showscores = false; + + if (chat_on) + { + // count down the scroll timer. + if (chat_scrolltime > 0) + chat_scrolltime--; + } + + if (netgame) + { + size_t i = 0; + + // handle spam while we're at it: + for(; (i<MAXPLAYERS); i++) + { + if (stop_spamming[i] > 0) + stop_spamming[i]--; + } + + // handle chat timers + for (i=0; (i<chat_nummsg_min); i++) + { + if (chat_timers[i] > 0) + chat_timers[i]--; + else + HU_removeChatText_Mini(); + } + } + + if (cechotimer > 0) --cechotimer; + + if (hu_redownloadinggamestate) + resynch_ticker++; } #ifndef NONET @@ -1102,7 +1142,7 @@ boolean HU_Responder(event_t *ev) if (chatlen+pastelen > HU_MAXMSGLEN) return true; // we can't paste this!! - memmove(&w_chat[c_input + pastelen], &w_chat[c_input], pastelen); + memmove(&w_chat[c_input + pastelen], &w_chat[c_input], (chatlen - c_input) + 1); // +1 for '\0' memcpy(&w_chat[c_input], paste, pastelen); // copy all of that. c_input += pastelen; return true; @@ -1854,8 +1894,6 @@ static void HU_DrawCEcho(void) echoptr = line; echoptr++; } - - --cechotimer; } static void HU_drawGametype(void) @@ -1917,9 +1955,6 @@ void HU_Drawer(void) // draw chat string plus cursor if (chat_on) { - // count down the scroll timer. - if (chat_scrolltime > 0) - chat_scrolltime--; if (!OLDCHAT) HU_DrawChat(); else @@ -1929,30 +1964,10 @@ void HU_Drawer(void) { 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. } - - 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) - { - size_t i = 0; - - // handle spam while we're at it: - for(; (i<MAXPLAYERS); i++) - { - if (stop_spamming[i] > 0) - stop_spamming[i]--; - } - - // handle chat timers - for (i=0; (i<chat_nummsg_min); i++) - { - if (chat_timers[i] > 0) - chat_timers[i]--; - else - HU_removeChatText_Mini(); - } - } #endif if (cechotimer) @@ -1979,7 +1994,13 @@ void HU_Drawer(void) } else HU_DrawCoopOverlay(); - LUA_HUDHOOK(scores); + + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_scores); + LUA_HUDHOOK(scores, luahuddrawlist_scores); + } + LUA_HUD_DrawList(luahuddrawlist_scores); } if (gamestate != GS_LEVEL) @@ -1992,12 +2013,9 @@ void HU_Drawer(void) // draw desynch text if (hu_redownloadinggamestate) { - static UINT32 resynch_ticker = 0; char resynch_text[14]; UINT32 i; - // Animate the dots - resynch_ticker++; strcpy(resynch_text, "Resynching"); for (i = 0; i < (resynch_ticker / 16) % 4; i++) strcat(resynch_text, "."); diff --git a/src/i_system.h b/src/i_system.h index 27fcdeb3f21d247e1001d12d7e8b1bee028e62a8..7153aa7357ad0a7d3e1cbedc84c5c929e5fd5fbd 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -42,23 +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 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); +UINT64 I_GetPrecisePrecision(void); + +/** \brief Get the current time in rendering tics, including fractions. +*/ +double I_GetFrameTime(void); -/** \brief The I_Sleep function +/** \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); /** \brief Get events diff --git a/src/i_time.c b/src/i_time.c new file mode 100644 index 0000000000000000000000000000000000000000..c1cc9dfd44d1c20828c598b44826fdbb48fed919 --- /dev/null +++ b/src/i_time.c @@ -0,0 +1,123 @@ +// 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 <math.h> + +#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; + +// A little more than the minimum sleep duration on Windows. +// May be incorrect for other platforms, but we don't currently have a way to +// query the scheduler granularity. SDL will do what's needed to make this as +// low as possible though. +#define MIN_SLEEP_DURATION_MS 2.1 + +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 * MIN_SLEEP_DURATION_MS)); + 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 0000000000000000000000000000000000000000..cab36133b6bf63ed43359f55f0c297d2a32b20c7 --- /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/i_video.h b/src/i_video.h index 638fcb6689f026722ffbb4e777111260ec54e97c..d66b2d95fcf8d91caf690ef2f76036fcf4e4b978 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -151,4 +151,6 @@ void I_BeginRead(void); */ void I_EndRead(void); +UINT32 I_GetRefreshRate(void); + #endif diff --git a/src/info.c b/src/info.c index 179370ca4c989965bee2bc9e65ac33fbc8b35c82..c4c197027347f01fee9bf07799f829e228e21d7b 100644 --- a/src/info.c +++ b/src/info.c @@ -770,9 +770,9 @@ state_t states[NUMSTATES] = // 1-Up box sprites (uses player sprite) {SPR_PLAY, SPR2_LIFE, 2, {NULL}, 0, 18, S_PLAY_BOX2}, // S_PLAY_BOX1 - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_PLAY_BOX1}, // S_PLAY_BOX2 + {SPR_NULL, 0, 1, {NULL}, 0, 18, S_PLAY_BOX1}, // S_PLAY_BOX2 {SPR_PLAY, SPR2_LIFE, 4, {NULL}, 0, 4, S_PLAY_ICON2}, // S_PLAY_ICON1 - {SPR_NULL, 0, 12, {NULL}, 0, 0, S_PLAY_ICON3}, // S_PLAY_ICON2 + {SPR_NULL, 0, 12, {NULL}, 0, 4, S_PLAY_ICON3}, // S_PLAY_ICON2 {SPR_PLAY, SPR2_LIFE, 20, {NULL}, 0, 4, S_NULL}, // S_PLAY_ICON3 // Level end sign (uses player sprite) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index da36142716c722701b59bb20479ec8e07a6512c4..5af9669ca3ab14672da929a900fcb6c448386fec 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -31,7 +31,7 @@ #include "m_misc.h" // M_MapNumber #include "b_bot.h" // B_UpdateBotleader #include "d_clisrv.h" // CL_RemovePlayer -#include "i_system.h" // I_GetPreciseTime, I_PreciseToMicros +#include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "lua_script.h" #include "lua_libs.h" @@ -1783,7 +1783,42 @@ static int lib_pTeleportMove(lua_State *L) INLEVEL if (!thing) return LUA_ErrInvalid(L, "mobj_t"); - lua_pushboolean(L, P_TeleportMove(thing, x, y, z)); + LUA_Deprecated(L, "P_TeleportMove", "P_SetOrigin\" or \"P_MoveOrigin"); + lua_pushboolean(L, P_MoveOrigin(thing, x, y, z)); + LUA_PushUserdata(L, tmthing, META_MOBJ); + P_SetTarget(&tmthing, ptmthing); + return 2; +} + +static int lib_pSetOrigin(lua_State *L) +{ + mobj_t *ptmthing = tmthing; + mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + fixed_t z = luaL_checkfixed(L, 4); + NOHUD + INLEVEL + if (!thing) + return LUA_ErrInvalid(L, "mobj_t"); + lua_pushboolean(L, P_SetOrigin(thing, x, y, z)); + LUA_PushUserdata(L, tmthing, META_MOBJ); + P_SetTarget(&tmthing, ptmthing); + return 2; +} + +static int lib_pMoveOrigin(lua_State *L) +{ + mobj_t *ptmthing = tmthing; + mobj_t *thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + fixed_t z = luaL_checkfixed(L, 4); + NOHUD + INLEVEL + if (!thing) + return LUA_ErrInvalid(L, "mobj_t"); + lua_pushboolean(L, P_MoveOrigin(thing, x, y, z)); LUA_PushUserdata(L, tmthing, META_MOBJ); P_SetTarget(&tmthing, ptmthing); return 2; @@ -3909,7 +3944,7 @@ static int lib_gTicsToMilliseconds(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; } @@ -4048,6 +4083,8 @@ static luaL_Reg lib[] = { {"P_TryMove",lib_pTryMove}, {"P_Move",lib_pMove}, {"P_TeleportMove",lib_pTeleportMove}, + {"P_SetOrigin",lib_pSetOrigin}, + {"P_MoveOrigin",lib_pMoveOrigin}, {"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 fc6a5f4ee4e4c4aa92683126c8b939c5ee36d2c3..5a14294c3b312f2cac04ce434e4af2d92c713b5d 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -14,6 +14,7 @@ #include "d_player.h" #include "s_sound.h" #include "d_event.h" +#include "lua_hudlib_drawlist.h" /* Do you know what an 'X Macro' is? Such a macro is called over each element of @@ -110,12 +111,12 @@ ENUM (STRING_HOOK); /* dead simple, LUA_HOOK(GameQuit) */ #define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) -#define LUA_HUDHOOK(type) LUA_HookHUD(HUD_HOOK(type)) +#define LUA_HUDHOOK(type,drawlist) LUA_HookHUD(HUD_HOOK(type),(drawlist)) extern boolean hook_cmd_running; void LUA_HookVoid(int hook); -void LUA_HookHUD(int hook); +void LUA_HookHUD(int hook, huddrawlist_h drawlist); int LUA_HookMobj(mobj_t *, int hook); int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 48980f4a4c1f809b8bf296d48c0332ea682e5234..0b24b7b535b917fa095510db4564d2564688a508 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -641,7 +641,7 @@ int LUA_HookKey(event_t *event, int hook_type) return hook.status; } -void LUA_HookHUD(int hook_type) +void LUA_HookHUD(int hook_type, huddrawlist_h list) { const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; @@ -650,12 +650,15 @@ void LUA_HookHUD(int hook_type) start_hook_stack(); begin_hook_values(&hook); - LUA_SetHudHook(hook_type); + LUA_SetHudHook(hook_type, list); hud_running = true; // local hook init_hook_call(&hook, 0, res_none); call_mapped(&hook, map); hud_running = false; + + lua_pushnil(gL); + lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); } } diff --git a/src/lua_hud.h b/src/lua_hud.h index ad2b51d3ef7d58b4df71ace0c18a9560962e947e..0d629d233d0eeb10d7a0591e8cd95ab357e4aecb 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, @@ -47,4 +52,6 @@ extern boolean hud_running; boolean LUA_HudEnabled(enum hud option); -void LUA_SetHudHook(int hook); +void LUA_SetHudHook(int hook, huddrawlist_h list); + +#endif // __LUA_HUD_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index c7f2bbc28ec31d6266aa9eb435248663a4418232..40c6f6e1fc0e8ad901d31adfdbea28b559e3860b 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -645,7 +645,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); @@ -659,7 +660,14 @@ static int libd_draw(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, flags, patch, colormap); + 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_AddDraw(list, x, y, patch, flags, colormap); + else + V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, flags, patch, colormap); return 0; } @@ -668,7 +676,8 @@ static int libd_drawScaled(lua_State *L) fixed_t x, y, scale; INT32 flags; patch_t *patch; - const UINT8 *colormap = NULL; + UINT8 *colormap = NULL; + huddrawlist_h list; HUDONLY x = luaL_checkinteger(L, 1); @@ -685,7 +694,14 @@ static int libd_drawScaled(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawFixedPatch(x, y, scale, flags, patch, colormap); + 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_AddDrawScaled(list, x, y, scale, patch, flags, colormap); + else + V_DrawFixedPatch(x, y, scale, flags, patch, colormap); return 0; } @@ -694,7 +710,8 @@ static int libd_drawStretched(lua_State *L) fixed_t x, y, hscale, vscale; INT32 flags; patch_t *patch; - const UINT8 *colormap = NULL; + UINT8 *colormap = NULL; + huddrawlist_h list; HUDONLY x = luaL_checkinteger(L, 1); @@ -712,7 +729,14 @@ static int libd_drawStretched(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawStretchyFixedPatch(x, y, hscale, vscale, flags, patch, colormap); + 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_AddDrawStretched(list, x, y, hscale, vscale, patch, flags, colormap); + else + V_DrawStretchyFixedPatch(x, y, hscale, vscale, flags, patch, colormap); return 0; } @@ -721,7 +745,8 @@ static int libd_drawCropped(lua_State *L) fixed_t x, y, hscale, vscale, sx, sy, w, h; INT32 flags; patch_t *patch; - const UINT8 *colormap = NULL; + UINT8 *colormap = NULL; + huddrawlist_h list; HUDONLY x = luaL_checkinteger(L, 1); @@ -751,13 +776,22 @@ static int libd_drawCropped(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawCroppedPatch(x, y, hscale, vscale, flags, patch, colormap, sx, sy, w, h); + 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_AddDrawCropped(list, x, y, hscale, vscale, patch, flags, colormap, sx, sy, w, h); + else + V_DrawCroppedPatch(x, y, hscale, vscale, flags, patch, colormap, sx, sy, w, h); return 0; } static int libd_drawNum(lua_State *L) { INT32 x, y, flags, num; + huddrawlist_h list; + HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); @@ -765,13 +799,22 @@ static int libd_drawNum(lua_State *L) flags = luaL_optinteger(L, 4, 0); flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawTallNum(x, y, flags, num); + 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_AddDrawNum(list, x, y, num, flags); + else + V_DrawTallNum(x, y, flags, num); return 0; } static int libd_drawPaddedNum(lua_State *L) { INT32 x, y, flags, num, digits; + huddrawlist_h list; + HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); @@ -780,12 +823,20 @@ static int libd_drawPaddedNum(lua_State *L) flags = luaL_optinteger(L, 5, 0); flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawPaddedTallNum(x, y, flags, num, digits); + 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_AddDrawPaddedNum(list, x, y, num, digits, flags); + else + V_DrawPaddedTallNum(x, y, flags, num, digits); return 0; } static int libd_drawFill(lua_State *L) { + huddrawlist_h list; INT32 x = luaL_optinteger(L, 1, 0); INT32 y = luaL_optinteger(L, 2, 0); INT32 w = luaL_optinteger(L, 3, BASEVIDWIDTH); @@ -793,12 +844,21 @@ static int libd_drawFill(lua_State *L) INT32 c = luaL_optinteger(L, 5, 31); HUDONLY - V_DrawFill(x, y, w, h, c); + + 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_AddDrawFill(list, x, y, w, h, c); + else + V_DrawFill(x, y, w, h, c); 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); @@ -808,6 +868,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 @@ -899,6 +968,7 @@ static int libd_drawNameTag(lua_State *L) UINT16 outlinecolor; UINT8 *basecolormap = NULL; UINT8 *outlinecolormap = NULL; + huddrawlist_h list; HUDONLY @@ -914,7 +984,15 @@ static int libd_drawNameTag(lua_State *L) outlinecolormap = R_GetTranslationColormap(TC_DEFAULT, outlinecolor, GTC_CACHE); flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawNameTag(x, y, flags, FRACUNIT, basecolormap, outlinecolormap, 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_AddDrawNameTag(list, x, y, str, flags, basecolor, outlinecolor, basecolormap, outlinecolormap); + else + V_DrawNameTag(x, y, flags, FRACUNIT, basecolormap, outlinecolormap, str); return 0; } @@ -929,6 +1007,7 @@ static int libd_drawScaledNameTag(lua_State *L) UINT16 outlinecolor; UINT8 *basecolormap = NULL; UINT8 *outlinecolormap = NULL; + huddrawlist_h list; HUDONLY @@ -947,7 +1026,15 @@ static int libd_drawScaledNameTag(lua_State *L) outlinecolormap = R_GetTranslationColormap(TC_DEFAULT, outlinecolor, GTC_CACHE); flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawNameTag(FixedInt(x), FixedInt(y), flags, scale, basecolormap, outlinecolormap, 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_AddDrawScaledNameTag(list, x, y, str, flags, scale, basecolor, outlinecolor, basecolormap, outlinecolormap); + else + V_DrawNameTag(FixedInt(x), FixedInt(y), flags, scale, basecolormap, outlinecolormap, str); return 0; } @@ -957,6 +1044,7 @@ static int libd_drawLevelTitle(lua_State *L) INT32 y; const char *str; INT32 flags; + huddrawlist_h list; HUDONLY @@ -967,7 +1055,14 @@ static int libd_drawLevelTitle(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawLevelTitle(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_AddDrawLevelTitle(list, x, y, str, flags); + else + V_DrawLevelTitle(x, y, flags, str); return 0; } @@ -1060,6 +1155,7 @@ static int libd_getStringColormap(lua_State *L) static int libd_fadeScreen(lua_State *L) { + huddrawlist_h list; UINT16 color = luaL_checkinteger(L, 1); UINT8 strength = luaL_checkinteger(L, 2); const UINT8 maxstrength = ((color & 0xFF00) ? 32 : 10); @@ -1072,13 +1168,24 @@ static int libd_fadeScreen(lua_State *L) if (strength > 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; } @@ -1356,10 +1463,13 @@ boolean LUA_HudEnabled(enum hud option) return false; } -void LUA_SetHudHook(int hook) +void LUA_SetHudHook(int hook, huddrawlist_h list) { lua_getref(gL, lib_draw_ref); + lua_pushlightuserdata(gL, list); + lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + switch (hook) { case HUD_HOOK(game): { diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c new file mode 100644 index 0000000000000000000000000000000000000000..bcf132ec69e7fd706f55cb19c30dd543616a1554 --- /dev/null +++ b/src/lua_hudlib_drawlist.c @@ -0,0 +1,572 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2014-2016 by John "JTE" Muniz. +// Copyright (C) 2014-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.c +/// \brief a data structure for managing cached drawlists for the Lua hud lib + +#include "lua_hudlib_drawlist.h" + +#include <string.h> + +#include "v_video.h" +#include "z_zone.h" + +enum drawitem_e { + DI_Draw = 0, + DI_DrawScaled, + DI_DrawStretched, + DI_DrawCropped, + DI_DrawNum, + DI_DrawPaddedNum, + DI_DrawFill, + DI_DrawString, + DI_DrawNameTag, + DI_DrawScaledNameTag, + DI_DrawLevelTitle, + DI_FadeScreen, + 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; +} 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_fixed, + align_fixedcenter, + align_fixedright, + align_small, + align_smallfixed, + align_smallfixedcenter, + align_smallfixedright, + align_smallcenter, + align_smallright, + align_smallthin, + align_smallthincenter, + align_smallthinright, + align_smallthinfixed, + align_smallthinfixedcenter, + align_smallthinfixedright, + align_thin, + align_thinfixed, + align_thinfixedcenter, + align_thinfixedright, + 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_AddDrawCropped( + huddrawlist_h list, + fixed_t x, + fixed_t y, + fixed_t hscale, + fixed_t vscale, + patch_t *patch, + INT32 flags, + UINT8 *colormap, + fixed_t sx, + fixed_t sy, + fixed_t w, + fixed_t h +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawCropped; + item->x = x; + item->y = y; + item->hscale = hscale; + item->vscale = vscale; + item->patch = patch; + item->flags = flags; + item->colormap = colormap; + item->sx = sx; + item->sy = sy; + item->w = w; + item->h = h; +} + +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_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_AddDrawNameTag( + huddrawlist_h list, + INT32 x, + INT32 y, + const char *str, + INT32 flags, + UINT16 basecolor, + UINT16 outlinecolor, + UINT8 *basecolormap, + UINT8 *outlinecolormap +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawNameTag; + item->x = x; + item->y = y; + item->str = CopyString(list, str); + item->flags = flags; + item->basecolor = basecolor; + item->outlinecolor = outlinecolor; + item->basecolormap = basecolormap; + item->outlinecolormap = outlinecolormap; +} + +void LUA_HUD_AddDrawScaledNameTag( + huddrawlist_h list, + fixed_t x, + fixed_t y, + const char *str, + INT32 flags, + fixed_t scale, + UINT16 basecolor, + UINT16 outlinecolor, + UINT8 *basecolormap, + UINT8 *outlinecolormap +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawScaledNameTag; + item->x = x; + item->y = y; + item->str = CopyString(list, str); + item->flags = flags; + item->scale = scale; + item->basecolor = basecolor; + item->outlinecolor = outlinecolor; + item->basecolormap = basecolormap; + item->outlinecolormap = outlinecolormap; +} + +void LUA_HUD_AddDrawLevelTitle( + huddrawlist_h list, + INT32 x, + INT32 y, + const char *str, + INT32 flags +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawLevelTitle; + item->x = x; + item->y = y; + item->str = CopyString(list, str); + item->flags = flags; +} + +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_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<<FRACBITS, item->y<<FRACBITS, FRACUNIT, item->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_DrawCropped: + V_DrawCroppedPatch(item->x, item->y, item->hscale, item->vscale, item->flags, item->patch, item->colormap, item->sx, item->sy, item->w, item->h); + 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_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; + case align_fixed: + V_DrawStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_fixedcenter: + V_DrawCenteredStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_fixedright: + V_DrawRightAlignedStringAtFixed(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_smallfixed: + V_DrawSmallStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_smallfixedcenter: + V_DrawCenteredSmallStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_smallfixedright: + V_DrawRightAlignedSmallStringAtFixed(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; + case align_smallthin: + V_DrawSmallThinString(item->x, item->y, item->flags, item->str); + break; + case align_smallthincenter: + V_DrawCenteredSmallThinString(item->x, item->y, item->flags, item->str); + break; + case align_smallthinright: + V_DrawRightAlignedSmallThinString(item->x, item->y, item->flags, item->str); + break; + case align_smallthinfixed: + V_DrawSmallThinStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_smallthinfixedcenter: + V_DrawCenteredSmallThinStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_smallthinfixedright: + V_DrawRightAlignedSmallThinStringAtFixed(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; + case align_thinfixed: + V_DrawThinStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_thinfixedcenter: + V_DrawCenteredThinStringAtFixed(item->x, item->y, item->flags, item->str); + break; + case align_thinfixedright: + V_DrawRightAlignedThinStringAtFixed(item->x, item->y, item->flags, item->str); + break; + } + break; + case DI_DrawNameTag: + V_DrawNameTag(item->x, item->y, item->flags, FRACUNIT, item->basecolormap, item->outlinecolormap, item->str); + break; + case DI_DrawScaledNameTag: + V_DrawNameTag(FixedInt(item->x), FixedInt(item->y), item->flags, item->scale, item->basecolormap, item->outlinecolormap, item->str); + break; + case DI_DrawLevelTitle: + V_DrawLevelTitle(item->x, item->y, item->flags, item->str); + break; + case DI_FadeScreen: + V_DrawFadeScreen(item->color, item->strength); + 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 0000000000000000000000000000000000000000..5e97b5c2653bd5dcded976fe5e274a4ec1d31cd7 --- /dev/null +++ b/src/lua_hudlib_drawlist.h @@ -0,0 +1,152 @@ +// 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_AddDrawCropped( + huddrawlist_h list, + fixed_t x, + fixed_t y, + fixed_t hscale, + fixed_t vscale, + patch_t *patch, + INT32 flags, + UINT8 *colormap, + fixed_t sx, + fixed_t sy, + fixed_t w, + fixed_t h +); +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_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_AddDrawNameTag( + huddrawlist_h list, + INT32 x, + INT32 y, + const char *str, + INT32 flags, + UINT16 basecolor, + UINT16 outlinecolor, + UINT8 *basecolormap, + UINT8 *outlinecolormap +); +void LUA_HUD_AddDrawScaledNameTag( + huddrawlist_h list, + fixed_t x, + fixed_t y, + const char *str, + INT32 flags, + fixed_t scale, + UINT16 basecolor, + UINT16 outlinecolor, + UINT8 *basecolormap, + UINT8 *outlinecolormap +); +void LUA_HUD_AddDrawLevelTitle( + huddrawlist_h list, + INT32 x, + INT32 y, + const char *str, + INT32 flags +); +void LUA_HUD_AddFadeScreen( + huddrawlist_h list, + UINT16 color, + UINT8 strength +); + +// 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 953b390006c3419be9a63c2a925d112716568eb4..2aec48c907886a26dc8e22fe1ce58ee5f9df4a98 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -458,7 +458,7 @@ static int mobj_get(lua_State *L) } #define NOSET luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly.", mobj_opt[field]) -#define NOSETPOS luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_Move") ", " LUA_QL("P_TryMove") ", or " LUA_QL("P_TeleportMove") " instead.", mobj_opt[field]) +#define NOSETPOS luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_Move") ", " LUA_QL("P_TryMove") ", or " LUA_QL("P_SetOrigin") ", or " LUA_QL("P_MoveOrigin") " instead.", mobj_opt[field]) static int mobj_set(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -767,6 +767,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 b3a1d0fe22304faea9f7b9656bd9dcd4e7a872a9..41765e6e1cc415e81c7a0ab9f9b8f893eb993df0 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_cheat.c b/src/m_cheat.c index 40b9a1230eb439e5ef1afff546e0368e66514bbc..89c8009aed321a0535d3e9921432fe59b09a68ee 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -475,7 +475,7 @@ void Command_RTeleport_f(void) CONS_Printf(M_GetText("Teleporting by %d, %d, %d...\n"), intx, inty, FixedInt((intz-p->mo->z))); P_MapStart(); - if (!P_TeleportMove(p->mo, p->mo->x+intx*FRACUNIT, p->mo->y+inty*FRACUNIT, intz)) + if (!P_SetOrigin(p->mo, p->mo->x+intx*FRACUNIT, p->mo->y+inty*FRACUNIT, intz)) CONS_Alert(CONS_WARNING, M_GetText("Unable to teleport to that spot!\n")); else S_StartSound(p->mo, sfx_mixup); @@ -696,7 +696,7 @@ void Command_Teleport_f(void) } P_MapStart(); - if (!P_TeleportMove(p->mo, intx, inty, intz)) + if (!P_SetOrigin(p->mo, intx, inty, intz)) CONS_Alert(CONS_WARNING, M_GetText("Unable to teleport to that spot!\n")); else S_StartSound(p->mo, sfx_mixup); @@ -1315,13 +1315,13 @@ void OP_ObjectplaceMovement(player_t *player) if (cmd->forwardmove != 0) { P_Thrust(player->mo, player->mo->angle, (cmd->forwardmove*player->mo->scale/MAXPLMOVE)*cv_speed.value); - P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); + P_MoveOrigin(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); player->mo->momx = player->mo->momy = 0; } if (cmd->sidemove != 0) { P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*player->mo->scale/MAXPLMOVE)*cv_speed.value); - P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); + P_MoveOrigin(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z); player->mo->momx = player->mo->momy = 0; } diff --git a/src/m_menu.c b/src/m_menu.c index 9daab767f43e98f4c642fcd0700a217473d2e7c0..3a5a1a13a8bbc2972b21b74df1a11fc70faacbd9 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" @@ -171,10 +173,10 @@ static INT32 vidm_nummodes; static INT32 vidm_column_size; // new menus -static tic_t recatkdrawtimer = 0; -static tic_t ntsatkdrawtimer = 0; +static fixed_t recatkdrawtimer = 0; +static fixed_t ntsatkdrawtimer = 0; -static tic_t charseltimer = 0; +static fixed_t charseltimer = 0; static fixed_t char_scroll = 0; #define charscrollamt 128*FRACUNIT @@ -207,17 +209,17 @@ menu_t SPauseDef; static levelselect_t levelselect = {0, NULL}; static UINT8 levelselectselect[3]; static patch_t *levselp[2][3]; -static INT32 lsoffs[2]; +static fixed_t lsoffs[2]; #define lsrow levelselectselect[0] #define lscol levelselectselect[1] #define lshli levelselectselect[2] #define lshseperation 101 -#define lsbasevseperation (62*vid.height)/(BASEVIDHEIGHT*vid.dupy) //62 +#define lsbasevseperation ((62*vid.height)/(BASEVIDHEIGHT*vid.dupy)) //62 #define lsheadingheight 16 #define getheadingoffset(row) (levelselect.rows[row].header[0] ? lsheadingheight : 0) -#define lsvseperation(row) lsbasevseperation + getheadingoffset(row) +#define lsvseperation(row) (lsbasevseperation + getheadingoffset(row)) #define lswide(row) levelselect.rows[row].mapavailable[3] #define lsbasex 19 @@ -1386,6 +1388,7 @@ static menuitem_t OP_VideoOptionsMenu[] = #ifdef HWRENDER {IT_HEADER, NULL, "Renderer", NULL, 208}, {IT_CALL | IT_STRING, NULL, "OpenGL Options...", M_OpenGLOptionsMenu, 214}, + {IT_STRING | IT_CVAR, NULL, "FPS Cap", &cv_fpscap, 219}, #endif }; @@ -5496,7 +5499,7 @@ static void M_HandleLevelPlatter(INT32 choice) { if (!lsoffs[0]) // prevent sound spam { - lsoffs[0] = -8; + lsoffs[0] = -8 * FRACUNIT; S_StartSound(NULL,sfx_s3kb7); } return; @@ -5505,7 +5508,7 @@ static void M_HandleLevelPlatter(INT32 choice) } lsrow++; - lsoffs[0] = lsvseperation(lsrow); + lsoffs[0] = lsvseperation(lsrow) * FRACUNIT; if (levelselect.rows[lsrow].header[0]) lshli = lsrow; @@ -5524,7 +5527,7 @@ static void M_HandleLevelPlatter(INT32 choice) { if (!lsoffs[0]) // prevent sound spam { - lsoffs[0] = 8; + lsoffs[0] = 8 * FRACUNIT; S_StartSound(NULL,sfx_s3kb7); } return; @@ -5533,7 +5536,7 @@ static void M_HandleLevelPlatter(INT32 choice) } lsrow--; - lsoffs[0] = -lsvseperation(iter); + lsoffs[0] = -lsvseperation(iter) * FRACUNIT; if (levelselect.rows[lsrow].header[0]) lshli = lsrow; @@ -5574,7 +5577,7 @@ static void M_HandleLevelPlatter(INT32 choice) } else if (!lsoffs[0]) // prevent sound spam { - lsoffs[0] = -8; + lsoffs[0] = -8 * FRACUNIT; S_StartSound(NULL,sfx_s3kb2); } break; @@ -5600,14 +5603,14 @@ static void M_HandleLevelPlatter(INT32 choice) { lscol++; - lsoffs[1] = (lswide(lsrow) ? 8 : -lshseperation); + lsoffs[1] = (lswide(lsrow) ? 8 : -lshseperation) * FRACUNIT; S_StartSound(NULL,sfx_s3kb7); ifselectvalnextmap(lscol) else ifselectvalnextmap(0) } else if (!lsoffs[1]) // prevent sound spam { - lsoffs[1] = 8; + lsoffs[1] = 8 * FRACUNIT; S_StartSound(NULL,sfx_s3kb7); } break; @@ -5632,14 +5635,14 @@ static void M_HandleLevelPlatter(INT32 choice) { lscol--; - lsoffs[1] = (lswide(lsrow) ? -8 : lshseperation); + lsoffs[1] = (lswide(lsrow) ? -8 : lshseperation) * FRACUNIT; S_StartSound(NULL,sfx_s3kb7); ifselectvalnextmap(lscol) else ifselectvalnextmap(0) } else if (!lsoffs[1]) // prevent sound spam { - lsoffs[1] = -8; + lsoffs[1] = -8 * FRACUNIT; S_StartSound(NULL,sfx_s3kb7); } break; @@ -5802,7 +5805,7 @@ static void M_DrawRecordAttackForeground(void) for (i = -12; i < (BASEVIDHEIGHT/height) + 12; i++) { - INT32 y = ((i*height) - (height - ((recatkdrawtimer*2)%height))); + INT32 y = ((i*height) - (height - ((FixedInt(recatkdrawtimer*2))%height))); // don't draw above the screen { INT32 sy = FixedMul(y, dupz<<FRACBITS) >> FRACBITS; @@ -5819,17 +5822,18 @@ static void M_DrawRecordAttackForeground(void) } // draw clock - fa = (FixedAngle(((recatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK; + fa = (FixedAngle(((FixedInt(recatkdrawtimer * 4)) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK; V_DrawSciencePatch(160<<FRACBITS, (80<<FRACBITS) + (4*FINESINE(fa)), 0, clock, FRACUNIT); // Increment timer. - recatkdrawtimer++; + recatkdrawtimer += renderdeltatics; + if (recatkdrawtimer < 0) recatkdrawtimer = 0; } // NiGHTS Attack background. static void M_DrawNightsAttackMountains(void) { - static INT32 bgscrollx; + static fixed_t bgscrollx; INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); patch_t *background = W_CachePatchName(curbgname, PU_PATCH); INT16 w = background->width; @@ -5845,7 +5849,7 @@ static void M_DrawNightsAttackMountains(void) if (x < BASEVIDWIDTH) V_DrawScaledPatch(x, y, V_SNAPTOLEFT, background); - bgscrollx += (FRACUNIT/2); + bgscrollx += FixedMul(FRACUNIT/2, renderdeltatics); if (bgscrollx > w<<FRACBITS) bgscrollx &= 0xFFFF; } @@ -5876,7 +5880,7 @@ static void M_DrawNightsAttackBackground(void) M_DrawNightsAttackMountains(); // back top foreground patch - x = 0-(ntsatkdrawtimer%backtopwidth); + x = 0-(FixedInt(ntsatkdrawtimer)%backtopwidth); V_DrawScaledPatch(x, y, V_SNAPTOTOP|V_SNAPTOLEFT, backtopfg); for (i = 0; i < 3; i++) { @@ -5887,7 +5891,7 @@ static void M_DrawNightsAttackBackground(void) } // front top foreground patch - x = 0-((ntsatkdrawtimer*2)%fronttopwidth); + x = 0-(FixedInt(ntsatkdrawtimer*2)%fronttopwidth); V_DrawScaledPatch(x, y, V_SNAPTOTOP|V_SNAPTOLEFT, fronttopfg); for (i = 0; i < 3; i++) { @@ -5898,7 +5902,7 @@ static void M_DrawNightsAttackBackground(void) } // back bottom foreground patch - x = 0-(ntsatkdrawtimer%backbottomwidth); + x = 0-(FixedInt(ntsatkdrawtimer)%backbottomwidth); y = BASEVIDHEIGHT - backbottomheight; V_DrawScaledPatch(x, y, V_SNAPTOBOTTOM|V_SNAPTOLEFT, backbottomfg); for (i = 0; i < 3; i++) @@ -5910,7 +5914,7 @@ static void M_DrawNightsAttackBackground(void) } // front bottom foreground patch - x = 0-((ntsatkdrawtimer*2)%frontbottomwidth); + x = 0-(FixedInt(ntsatkdrawtimer*2)%frontbottomwidth); y = BASEVIDHEIGHT - frontbottomheight; V_DrawScaledPatch(x, y, V_SNAPTOBOTTOM|V_SNAPTOLEFT, frontbottomfg); for (i = 0; i < 3; i++) @@ -5922,7 +5926,8 @@ static void M_DrawNightsAttackBackground(void) } // Increment timer. - ntsatkdrawtimer++; + ntsatkdrawtimer += renderdeltatics; + if (ntsatkdrawtimer < 0) ntsatkdrawtimer = 0; } // NiGHTS Attack floating Super Sonic. @@ -5930,15 +5935,15 @@ static patch_t *ntssupersonic[2]; static void M_DrawNightsAttackSuperSonic(void) { const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE); - INT32 timer = (ntsatkdrawtimer/4) % 2; - angle_t fa = (FixedAngle(((ntsatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK; + INT32 timer = FixedInt(ntsatkdrawtimer/4) % 2; + angle_t fa = (FixedAngle((FixedInt(ntsatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK; V_DrawFixedPatch(235<<FRACBITS, (120<<FRACBITS) - (8*FINESINE(fa)), FRACUNIT, 0, ntssupersonic[timer], colormap); } static void M_DrawLevelPlatterMenu(void) { UINT8 iter = lsrow, sizeselect = (lswide(lsrow) ? 1 : 0); - INT32 y = lsbasey + lsoffs[0] - getheadingoffset(lsrow); + INT32 y = lsbasey + FixedInt(lsoffs[0]) - getheadingoffset(lsrow); const INT32 cursorx = (sizeselect ? 0 : (lscol*lshseperation)); if (currentMenu->prevMenu == &SP_TimeAttackDef) @@ -6006,7 +6011,7 @@ static void M_DrawLevelPlatterMenu(void) // draw cursor box if (levellistmode != LLM_CREATESERVER || lsrow) - V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey+lsoffs[0], 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)])); + V_DrawSmallScaledPatch(lsbasex + cursorx + FixedInt(lsoffs[1]), lsbasey+FixedInt(lsoffs[0]), 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)])); #if 0 if (levelselect.rows[lsrow].maplist[lscol] > 0) @@ -6014,13 +6019,26 @@ static void M_DrawLevelPlatterMenu(void) #endif // handle movement of cursor box - if (lsoffs[0] > 1 || lsoffs[0] < -1) - lsoffs[0] = 2*lsoffs[0]/3; + fixed_t cursormovefrac = FixedDiv(2, 3); + if (lsoffs[0] > FRACUNIT || lsoffs[0] < -FRACUNIT) + { + fixed_t offs = lsoffs[0]; + fixed_t newoffs = FixedMul(offs, cursormovefrac); + fixed_t deltaoffs = newoffs - offs; + newoffs = offs + FixedMul(deltaoffs, renderdeltatics); + lsoffs[0] = newoffs; + } else lsoffs[0] = 0; - if (lsoffs[1] > 1 || lsoffs[1] < -1) - lsoffs[1] = 2*lsoffs[1]/3; + if (lsoffs[1] > FRACUNIT || lsoffs[1] < -FRACUNIT) + { + fixed_t offs = lsoffs[1]; + fixed_t newoffs = FixedMul(offs, cursormovefrac); + fixed_t deltaoffs = newoffs - offs; + newoffs = offs + FixedMul(deltaoffs, renderdeltatics); + lsoffs[1] = newoffs; + } else lsoffs[1] = 0; @@ -7647,7 +7665,7 @@ static void M_HandleEmblemHints(INT32 choice) static musicdef_t *curplaying = NULL; static INT32 st_sel = 0, st_cc = 0; -static tic_t st_time = 0; +static fixed_t st_time = 0; static patch_t* st_radio[9]; static patch_t* st_launchpad[4]; @@ -7711,16 +7729,17 @@ static void M_DrawSoundTest(void) { if (cv_soundtest.value) { - frame[1] = (2-st_time); + frame[1] = (2 - (st_time >> FRACBITS)); frame[2] = ((cv_soundtest.value - 1) % 9); frame[3] += (((cv_soundtest.value - 1) / 9) % (FIRSTSUPERCOLOR - frame[3])); - if (st_time < 2) - st_time++; + if (st_time < (2 << FRACBITS)) + st_time += renderdeltatics; } } else { - if (curplaying->stoppingtics && st_time >= curplaying->stoppingtics) + fixed_t stoppingtics = (fixed_t)(curplaying->stoppingtics) << FRACBITS; + if (stoppingtics && st_time >= stoppingtics) { curplaying = NULL; st_time = 0; @@ -7731,11 +7750,11 @@ static void M_DrawSoundTest(void) angle_t ang; //bpm = FixedDiv((60*TICRATE)<<FRACBITS, bpm); -- bake this in on load - work = st_time<<FRACBITS; + work = st_time; work %= bpm; - if (st_time >= (FRACUNIT>>1)) // prevent overflow jump - takes about 15 minutes of loop on the same song to reach - st_time = (work>>FRACBITS); + if (st_time >= (FRACUNIT << (FRACBITS - 2))) // prevent overflow jump - takes about 15 minutes of loop on the same song to reach + st_time = work; work = FixedDiv(work*180, bpm); frame[0] = 8-(work/(20<<FRACBITS)); @@ -7746,7 +7765,7 @@ static void M_DrawSoundTest(void) hscale -= bounce/16; vscale += bounce/16; - st_time++; + st_time += renderdeltatics; } } } @@ -7786,7 +7805,7 @@ static void M_DrawSoundTest(void) V_DrawFill(y, 20, vid.width/vid.dupx, 24, 159); { - static fixed_t st_scroll = -1; + static fixed_t st_scroll = -FRACUNIT; const char* titl; x = 16; V_DrawString(x, 10, 0, "NOW PLAYING:"); @@ -7802,10 +7821,12 @@ static void M_DrawSoundTest(void) i = V_LevelNameWidth(titl); - if (++st_scroll >= i) - st_scroll %= i; + st_scroll += renderdeltatics; + + while (st_scroll >= (i << FRACBITS)) + st_scroll -= i << FRACBITS; - x -= st_scroll; + x -= st_scroll >> FRACBITS; while (x < BASEVIDWIDTH-y) x += i; @@ -8329,8 +8350,8 @@ static void M_StartTutorial(INT32 choice) // ============== static INT32 saveSlotSelected = 1; -static INT32 loadgamescroll = 0; -static UINT8 loadgameoffset = 0; +static fixed_t loadgamescroll = 0; +static fixed_t loadgameoffset = 0; static void M_CacheLoadGameData(void) { @@ -8355,14 +8376,14 @@ static void M_DrawLoadGameData(void) { prev_i = i; savetodraw = (saveSlotSelected + i + numsaves)%numsaves; - x = (BASEVIDWIDTH/2 - 42 + loadgamescroll) + (i*hsep); + x = (BASEVIDWIDTH/2 - 42 + FixedInt(loadgamescroll)) + (i*hsep); y = 33 + 9; { INT32 diff = x - (BASEVIDWIDTH/2 - 42); if (diff < 0) diff = -diff; - diff = (42 - diff)/3 - loadgameoffset; + diff = (42 - diff)/3 - FixedInt(loadgameoffset); if (diff < 0) diff = 0; y -= diff; @@ -8647,14 +8668,23 @@ skiplife: static void M_DrawLoad(void) { M_DrawMenuTitle(); + fixed_t scrollfrac = FixedDiv(2, 3); - if (loadgamescroll > 1 || loadgamescroll < -1) - loadgamescroll = 2*loadgamescroll/3; + if (loadgamescroll > FRACUNIT || loadgamescroll < -FRACUNIT) + { + fixed_t newscroll = FixedMul(loadgamescroll, scrollfrac); + fixed_t deltascroll = FixedMul(newscroll - loadgamescroll, renderdeltatics); + loadgamescroll += deltascroll; + } else loadgamescroll = 0; - if (loadgameoffset > 1) - loadgameoffset = 2*loadgameoffset/3; + if (loadgameoffset > FRACUNIT) + { + fixed_t newoffs = FixedMul(loadgameoffset, scrollfrac); + fixed_t deltaoffs = FixedMul(newoffs - loadgameoffset, renderdeltatics); + loadgameoffset += deltaoffs; + } else loadgameoffset = 0; @@ -8863,7 +8893,7 @@ static void M_ReadSaveStrings(void) UINT8 lastseen = 0; loadgamescroll = 0; - loadgameoffset = 14; + loadgameoffset = 14 * FRACUNIT; for (i = 1; (i < MAXSAVEGAMES); i++) // slot 0 is no save { @@ -8954,7 +8984,7 @@ static void M_HandleLoadSave(INT32 choice) ++saveSlotSelected; if (saveSlotSelected >= numsaves) saveSlotSelected -= numsaves; - loadgamescroll = 90; + loadgamescroll = 90 * FRACUNIT; break; case KEY_LEFTARROW: @@ -8962,7 +8992,7 @@ static void M_HandleLoadSave(INT32 choice) --saveSlotSelected; if (saveSlotSelected < 0) saveSlotSelected += numsaves; - loadgamescroll = -90; + loadgamescroll = -90 * FRACUNIT; break; case KEY_ENTER: @@ -8987,7 +9017,7 @@ static void M_HandleLoadSave(INT32 choice) else if (!loadgameoffset) { S_StartSound(NULL, sfx_lose); - loadgameoffset = 14; + loadgameoffset = 14 * FRACUNIT; } break; @@ -9013,7 +9043,7 @@ static void M_HandleLoadSave(INT32 choice) } else S_StartSound(NULL, sfx_lose); - loadgameoffset = 14; + loadgameoffset = 14 * FRACUNIT; } break; } @@ -9073,13 +9103,13 @@ static void M_LoadGame(INT32 choice) // void M_ForceSaveSlotSelected(INT32 sslot) { - loadgameoffset = 14; + loadgameoffset = 14 * FRACUNIT; // Already there? Whatever, then! if (sslot == saveSlotSelected) return; - loadgamescroll = 90; + loadgamescroll = 90 * FRACUNIT; if (saveSlotSelected <= numsaves/2) loadgamescroll = -loadgamescroll; @@ -9323,8 +9353,8 @@ static void M_DrawSetupChoosePlayerMenu(void) INT32 x, y; INT32 w = (vid.width/vid.dupx); - if (abs(char_scroll) > FRACUNIT) - char_scroll -= (char_scroll>>2); + if (abs(char_scroll) > FRACUNIT/4) + char_scroll -= FixedMul((char_scroll>>2), renderdeltatics); else // close enough. char_scroll = 0; // just be exact now. @@ -9352,7 +9382,7 @@ static void M_DrawSetupChoosePlayerMenu(void) // Don't render the title map hidetitlemap = true; - charseltimer++; + charseltimer += renderdeltatics; // Background and borders V_DrawFill(0, 0, bgwidth, vid.height, V_SNAPTOTOP|colormap[101]); @@ -9364,7 +9394,7 @@ static void M_DrawSetupChoosePlayerMenu(void) V_DrawFill(0, 0, bw, vid.height, V_NOSCALESTART|col); } - y = (charseltimer%32); + y = (charseltimer / FRACUNIT) % 32; V_DrawMappedPatch(0, y-bgheight, V_SNAPTOTOP, charbg, colormap); V_DrawMappedPatch(0, y, V_SNAPTOTOP, charbg, colormap); V_DrawMappedPatch(0, y+bgheight, V_SNAPTOTOP, charbg, colormap); @@ -10631,7 +10661,7 @@ static void M_Marathon(INT32 choice) titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please M_SetupNextMenu(&SP_MarathonDef); itemOn = marathonstart; // "Start" is selected. - recatkdrawtimer = 50-8; + recatkdrawtimer = (50-8) * FRACUNIT; char_scroll = 0; } @@ -10712,13 +10742,16 @@ void M_DrawMarathon(void) x = (((BASEVIDWIDTH-82)/2)+11)<<FRACBITS; y = (((BASEVIDHEIGHT-82)/2)+12-10)<<FRACBITS; - cnt = (36*(recatkdrawtimer<<FRACBITS))/TICRATE; + cnt = (36 * recatkdrawtimer) / TICRATE; fa = (FixedAngle(cnt)>>ANGLETOFINESHIFT) & FINEMASK; y -= (10*FINECOSINE(fa)); - recatkdrawtimer++; + if (renderisnewtic) + { + recatkdrawtimer += FRACUNIT; + } - soffset = cnt = (recatkdrawtimer%50); + soffset = cnt = ((recatkdrawtimer >> FRACBITS) % 50); if (!useBlackRock) { if (cnt > 8) @@ -10757,7 +10790,7 @@ void M_DrawMarathon(void) } w = char_scroll + (((8-cnt)*(8-cnt))<<(FRACBITS-5)); - if (soffset == 50-1) + if (soffset == 50-1 && renderisnewtic) w += FRACUNIT/2; { @@ -10812,11 +10845,11 @@ void M_DrawMarathon(void) if (!soffset) { - char_scroll += (360<<FRACBITS)/42; // like a clock, ticking at 42bpm! + char_scroll += (360 * renderdeltatics)/42; // like a clock, ticking at 42bpm! if (char_scroll >= 360<<FRACBITS) char_scroll -= 360<<FRACBITS; - if (recatkdrawtimer > (10*TICRATE)) - recatkdrawtimer -= (10*TICRATE); + if (recatkdrawtimer > ((10 << FRACBITS) * TICRATE)) + recatkdrawtimer -= ((10 << FRACBITS) * TICRATE); } M_DrawMenuTitle(); @@ -11028,7 +11061,7 @@ static INT32 menuRoomIndex = 0; static void M_DrawRoomMenu(void) { - static int frame = -12; + static fixed_t frame = -(12 << FRACBITS); int dot_frame; char text[4]; @@ -11039,7 +11072,7 @@ static void M_DrawRoomMenu(void) if (m_waiting_mode) { - dot_frame = frame / 4; + dot_frame = (int)(frame >> FRACBITS) / 4; dots = dot_frame + 3; strcpy(text, " "); @@ -11052,8 +11085,9 @@ static void M_DrawRoomMenu(void) strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame)); } - if (++frame == 12) - frame = -12; + frame += renderdeltatics; + while (frame >= (12 << FRACBITS)) + frame -= 12 << FRACBITS; currentMenu->menuitems[0].text = text; } @@ -11819,7 +11853,7 @@ static void M_HandleConnectIP(INT32 choice) // ======================== // Tails 03-02-2002 -static UINT8 multi_tics; +static fixed_t multi_tics; static UINT8 multi_frame; static UINT8 multi_spr2; @@ -11887,10 +11921,11 @@ static void M_DrawSetupMultiPlayerMenu(void) y += 11; // anim the player in the box - if (--multi_tics <= 0) + multi_tics -= renderdeltatics; + while (multi_tics <= 0) { multi_frame++; - multi_tics = 4; + multi_tics += 4*FRACUNIT; } #define charw 74 @@ -12161,7 +12196,7 @@ static void M_SetupMultiPlayer(INT32 choice) (void)choice; multi_frame = 0; - multi_tics = 4; + multi_tics = 4*FRACUNIT; strcpy(setupm_name, cv_playername.string); // set for player 1 @@ -12205,7 +12240,7 @@ static void M_SetupMultiPlayer2(INT32 choice) (void)choice; multi_frame = 0; - multi_tics = 4; + multi_tics = 4*FRACUNIT; strcpy (setupm_name, cv_playername2.string); // set for splitscreen secondary player @@ -13536,7 +13571,8 @@ void M_QuitResponse(INT32 ch) { V_DrawScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_PATCH)); // 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 bb5f2568728cf3f8db1d4677447d93e3692a39ba..d98c949cf476882199a0eefac99235086b71577d 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 9fc41000d24548deb4a58f3b5071c3f6a9fa77e1..9f65a7616abfe427eaa1abd69fe16c128d559fba 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -17,6 +17,7 @@ #include "i_system.h" #include "z_zone.h" #include "p_local.h" +#include "r_fps.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -122,6 +123,12 @@ perfstatrow_t commoncounter_rows[] = { {0} }; +perfstatrow_t interpolation_rows[] = { + {"intpfrc", "Interp frac: ", &ps_interp_frac, PS_TIME}, + {"intplag", "Interp lag: ", &ps_interp_lag, PS_TIME}, + {0} +}; + #ifdef HWRENDER perfstatrow_t batchcount_rows[] = { {"polygon", "Polygons: ", &ps_hw_numpolys, 0}, @@ -261,7 +268,7 @@ static INT32 PS_GetMetricAverage(ps_metric_t *metric, boolean time_metric) for (i = 0; i < cv_ps_samplesize.value; i++) { if (time_metric) - sum += I_PreciseToMicros(*((precise_t*)history_read_pos)); + sum += (*((precise_t*)history_read_pos)) / (I_GetPrecisePrecision() / 1000000); else sum += *((INT32*)history_read_pos); history_read_pos += value_size; @@ -281,7 +288,7 @@ static INT32 PS_GetMetricMinOrMax(ps_metric_t *metric, boolean time_metric, bool { INT32 value; if (time_metric) - value = I_PreciseToMicros(*((precise_t*)history_read_pos)); + value = (*((precise_t*)history_read_pos)) / (I_GetPrecisePrecision() / 1000000); else value = *((INT32*)history_read_pos); @@ -309,7 +316,7 @@ static INT32 PS_GetMetricSD(ps_metric_t *metric, boolean time_metric) { INT64 value; if (time_metric) - value = I_PreciseToMicros(*((precise_t*)history_read_pos)); + value = (*((precise_t*)history_read_pos)) / (I_GetPrecisePrecision() / 1000000); else value = *((INT32*)history_read_pos); @@ -339,7 +346,7 @@ static INT32 PS_GetMetricScreenValue(ps_metric_t *metric, boolean time_metric) else { if (time_metric) - return I_PreciseToMicros(metric->value.p); + return (metric->value.p) / (I_GetPrecisePrecision() / 1000000); else return metric->value.i; } @@ -473,6 +480,9 @@ static void PS_UpdateFrameStats(void) if (PS_IsLevelActive()) PS_UpdateRowHistories(commoncounter_rows, true); + if (R_UsingFrameInterpolation()) + PS_UpdateRowHistories(interpolation_rows, true); + #ifdef HWRENDER if (rendermode == render_opengl && cv_glbatching.value) { @@ -634,7 +644,7 @@ static void PS_DrawRenderStats(void) { const boolean hires = PS_HighResolution(); const int half_row = hires ? 5 : 4; - int x, y; + int x, y, cy = 10; PS_DrawDescriptorHeader(); @@ -645,7 +655,7 @@ static void PS_DrawRenderStats(void) if (PS_IsLevelActive()) { x = hires ? 115 : 90; - PS_DrawPerfRows(x, 10, V_BLUEMAP, commoncounter_rows); + cy = PS_DrawPerfRows(x, 10, V_BLUEMAP, commoncounter_rows) + half_row; #ifdef HWRENDER if (rendermode == render_opengl && cv_glbatching.value) @@ -659,6 +669,12 @@ static void PS_DrawRenderStats(void) } #endif } + + if (R_UsingFrameInterpolation()) + { + x = hires ? 115 : 90; + PS_DrawPerfRows(x, cy, V_ROSYMAP, interpolation_rows); + } } static void PS_DrawGameLogicStats(void) diff --git a/src/p_ceilng.c b/src/p_ceilng.c index d88d9be86a2416d9fa9ff7c7fa0a3a5d01743385..66f2dd58ecfba7e346d746166f31f8edb942c77a 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" @@ -334,6 +335,9 @@ INT32 EV_DoCeiling(mtag_t tag, line_t *line, ceiling_e type) ceiling->type = type; firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } @@ -398,6 +402,10 @@ INT32 EV_DoCrush(mtag_t tag, line_t *line, ceiling_e type) } 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_enemy.c b/src/p_enemy.c index 558b8a795fbdce896758d1fda49812691cc67f2b..8a5c2c743f2ca2136cc5ffc3d26c92306e0c824a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -1333,7 +1333,7 @@ void A_FaceStabHurl(mobj_t *actor) hwork->destscale = FixedSqrt(step*basesize); P_SetScale(hwork, hwork->destscale); hwork->fuse = 2; - P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<<FRACBITS))); + P_MoveOrigin(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<<FRACBITS))); step -= NUMGRADS; } @@ -2095,7 +2095,7 @@ void A_CrushclawAim(mobj_t *actor) #undef anglimit #undef angfactor - P_TeleportMove(actor, + P_MoveOrigin(actor, crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale), crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale), crab->z + locvar2*crab->scale); @@ -2233,7 +2233,7 @@ void A_CrushclawLaunch(mobj_t *actor) fixed_t idx = dx, idy = dy, idz = dz; while (chain) { - P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz); + P_MoveOrigin(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz); chain->movefactor = chain->z; idx += dx; idy += dy; @@ -11181,7 +11181,7 @@ void A_VileAttack(mobj_t *actor) // move the fire between the vile and the player //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); - P_TeleportMove(fire, + P_MoveOrigin(fire, actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), fire->z); @@ -11226,7 +11226,7 @@ void A_VileAttack(mobj_t *actor) // move the fire between the vile and the player //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); - P_TeleportMove(fire, + P_MoveOrigin(fire, actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)), fire->z); @@ -11893,12 +11893,12 @@ void A_FlickyCenter(mobj_t *actor) if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1) { actor->extravalue2 = 1; - P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z); + P_SetOrigin(actor, actor->target->x, actor->target->y, actor->target->z); } else if(actor->extravalue2) { actor->extravalue2 = 0; - P_TeleportMove(actor, originx, originy, originz); + P_SetOrigin(actor, originx, originy, originz); } } } @@ -12431,7 +12431,7 @@ void A_LightBeamReset(mobj_t *actor) actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128; actor->momz = (P_SignedRandom()*FRACUNIT)/128; - P_TeleportMove(actor, + P_SetOrigin(actor, actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2, actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2, actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2); @@ -12988,7 +12988,7 @@ void A_DoNPCSkid(mobj_t *actor) actor->momy = (2*actor->momy)/3; } - P_TeleportMove(actor, x, y, z); + P_MoveOrigin(actor, x, y, z); // Spawn a particle every 3 tics. if (!(leveltime % 3)) @@ -13329,7 +13329,7 @@ void A_Boss5MakeJunk(mobj_t *actor) if (locvar1 > 0) P_SetMobjState(broked, locvar1); if (!P_MobjWasRemoved(broked)) - P_TeleportMove(broked, broked->x + broked->momx, broked->y + broked->momy, broked->z); + P_MoveOrigin(broked, broked->x + broked->momx, broked->y + broked->momy, broked->z); ang += ANGLE_45; } @@ -13538,7 +13538,7 @@ void A_DustDevilThink(mobj_t *actor) //Chained thinker for the spiralling dust column. while (layer && !P_MobjWasRemoved(layer)) { angle_t fa = layer->angle >> ANGLETOFINESHIFT; - P_TeleportMove(layer, layer->x + 5 * FixedMul(scale, FINECOSINE(fa)), layer->y + 5 * FixedMul(scale, FINESINE(fa)), layer->z); + P_MoveOrigin(layer, layer->x + 5 * FixedMul(scale, FINECOSINE(fa)), layer->y + 5 * FixedMul(scale, FINESINE(fa)), layer->z); layer->scale = scale; layer->angle += ANG10 / 2; layer->momx = actor->momx; @@ -14547,7 +14547,7 @@ void A_DragonWing(mobj_t *actor) actor->angle = target->angle + actor->movedir; x = target->x + P_ReturnThrustX(actor, actor->angle, -target->radius); y = target->y + P_ReturnThrustY(actor, actor->angle, -target->radius); - P_TeleportMove(actor, x, y, target->z); + P_MoveOrigin(actor, x, y, target->z); } // Function: A_DragonSegment @@ -14588,7 +14588,7 @@ void A_DragonSegment(mobj_t *actor) zdist = P_ReturnThrustY(target, zangle, radius); actor->angle = hangle; - P_TeleportMove(actor, target->x + xdist, target->y + ydist, target->z + zdist); + P_MoveOrigin(actor, target->x + xdist, target->y + ydist, target->z + zdist); } // Function: A_ChangeHeight diff --git a/src/p_floor.c b/src/p_floor.c index d9e0ee0f267722c4055cdbc133b7face082090fd..8a4a87f69fd33dc1830fe7f7e0b291252bb62cfb 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -17,6 +17,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" @@ -523,6 +524,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 @@ -1709,6 +1712,9 @@ void EV_DoFloor(mtag_t tag, line_t *line, floor_e floortype) } firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&dofloor->thinker, sec, false); } } @@ -1805,6 +1811,10 @@ void EV_DoElevator(mtag_t tag, line_t *line, elevator_e elevtype) } elevator->ceilingdestheight = elevator->floordestheight + sec->ceilingheight - sec->floorheight; + + // interpolation + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, true); } } @@ -1953,6 +1963,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 @@ -1978,6 +1992,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) @@ -2030,6 +2048,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]; @@ -2081,6 +2103,10 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) block->ceilingstartheight = block->sector->ceilingheight; block->tag = (INT16)rover->master->args[0]; + // interpolation + R_CreateInterpolator_SectorPlane(&block->thinker, roversec, false); + R_CreateInterpolator_SectorPlane(&block->thinker, roversec, true); + if (itsamonitor) { oldx = thing->x; @@ -2089,9 +2115,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) @@ -2112,9 +2138,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_local.h b/src/p_local.h index f50606117cd91566d3a9f2dc46de434efc66c6d2..49af03f3666a29ec703beb7dc4a0ae8fa673b297 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -91,6 +91,7 @@ typedef struct camera_s // Camera demobjerization // Info for drawing: position. fixed_t x, y, z; + boolean reset; //More drawing info: to determine current sprite. angle_t angle; // orientation @@ -413,7 +414,8 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); boolean P_Move(mobj_t *actor, fixed_t speed); -boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); +boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); +boolean P_MoveOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); void P_SlideMove(mobj_t *mo); void P_BounceMove(mobj_t *mo); boolean P_CheckSight(mobj_t *t1, mobj_t *t2); diff --git a/src/p_map.c b/src/p_map.c index 2bb02503f464fde0b166e6940e9c40ad166a171c..117d49ea491e05310d4c358f7708d8d2b9473d4c 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" @@ -74,7 +75,7 @@ camera_t *mapcampointer; // // P_TeleportMove // -boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) +static boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) { // the move is ok, // so link the thing into its new position @@ -106,6 +107,30 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) return true; } +// P_SetOrigin - P_TeleportMove which RESETS interpolation values. +// +boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) +{ + boolean result = P_TeleportMove(thing, x, y, z); + + if (result == true) + { + thing->old_x = thing->x; + thing->old_y = thing->y; + thing->old_z = thing->z; + } + + return result; +} + +// +// P_MoveOrigin - P_TeleportMove which KEEPS interpolation values. +// +boolean P_MoveOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) +{ + return P_TeleportMove(thing, x, y, z); +} + // ========================================================================= // MOVEMENT ITERATOR FUNCTIONS // ========================================================================= @@ -1153,9 +1178,9 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // underneath if (tmthing->eflags & MFE_VERTICALFLIP) - P_TeleportMove(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale)); + P_SetOrigin(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale)); else - P_TeleportMove(thing, thing->x, thing->y, 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, DMG_SPIKE); return true; @@ -1915,7 +1940,7 @@ static boolean PIT_CheckLine(line_t *ld) cosradius = FixedMul(dist, FINECOSINE(langle>>ANGLETOFINESHIFT)); sinradius = FixedMul(dist, FINESINE(langle>>ANGLETOFINESHIFT)); tmthing->flags |= MF_NOCLIP; - P_TeleportMove(tmthing, result.x + cosradius - tmthing->momx, result.y + sinradius - tmthing->momy, tmthing->z); + P_MoveOrigin(tmthing, result.x + cosradius - tmthing->momx, result.y + sinradius - tmthing->momy, tmthing->z); tmthing->flags &= ~MF_NOCLIP; } #endif diff --git a/src/p_mobj.c b/src/p_mobj.c index 2dbfac3bd0c5462b276e833f11d3dfe20eba99cc..9bb68b1e81ef2d0d36727a1d70b9b20d14925132 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -19,6 +19,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" @@ -4023,15 +4024,22 @@ void P_NullPrecipThinker(precipmobj_t *mobj) void P_SnowThinker(precipmobj_t *mobj) { + R_ResetPrecipitationMobjInterpolationState(mobj); + P_CycleStateAnimation((mobj_t *)mobj); // adjust height if ((mobj->z += mobj->momz) <= mobj->floorz) + { mobj->z = mobj->ceilingz; + R_ResetPrecipitationMobjInterpolationState(mobj); + } } void P_RainThinker(precipmobj_t *mobj) { + R_ResetPrecipitationMobjInterpolationState(mobj); + P_CycleStateAnimation((mobj_t *)mobj); if (mobj->state != &states[S_RAIN1]) @@ -4051,6 +4059,7 @@ void P_RainThinker(precipmobj_t *mobj) return; mobj->z = mobj->ceilingz; + R_ResetPrecipitationMobjInterpolationState(mobj); P_SetPrecipMobjState(mobj, S_RAIN1); return; @@ -4657,7 +4666,7 @@ static void P_Boss4MoveSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz) while ((base = base->tracer)) { for (seg = base, dist = 172*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 124*FRACUNIT, --s) - P_TeleportMove(seg, mobj->x + P_ReturnThrustX(mobj, angle, dist), mobj->y + P_ReturnThrustY(mobj, angle, dist), bz + FixedMul(fz, FixedDiv(s<<FRACBITS, 9<<FRACBITS))); + P_MoveOrigin(seg, mobj->x + P_ReturnThrustX(mobj, angle, dist), mobj->y + P_ReturnThrustY(mobj, angle, dist), bz + FixedMul(fz, FixedDiv(s<<FRACBITS, 9<<FRACBITS))); angle += ANGLE_MAX/3; } } @@ -5578,7 +5587,7 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj->hprev->destscale = FRACUNIT + (2*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2); P_SetScale(mobj->hprev, mobj->hprev->destscale); - P_TeleportMove(mobj->hprev, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->hprev->height/2); + P_MoveOrigin(mobj->hprev, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->hprev->height/2); mobj->hprev->momx = mobj->momx; mobj->hprev->momy = mobj->momy; mobj->hprev->momz = mobj->momz; @@ -6433,7 +6442,7 @@ void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on y if (dist < source->movefactor) { source->momx = source->momy = source->momz = 0; - P_TeleportMove(source, tx, ty, tz); + P_MoveOrigin(source, tx, ty, tz); } else { @@ -7042,7 +7051,7 @@ static void P_UpdateMinecartSegments(mobj_t *mobj) dx = seg->extravalue1; dy = seg->extravalue2; sang = seg->cusval; - P_TeleportMove(seg, x + s*dx + c*dy, y - c*dx + s*dy, z); + P_MoveOrigin(seg, x + s*dx + c*dy, y - c*dx + s*dy, z); seg->angle = ang + FixedAngle(FRACUNIT*sang); seg->flags2 = (seg->flags2 & ~MF2_DONTDRAW) | (mobj->flags2 & MF2_DONTDRAW); seg = seg->tracer; @@ -7809,6 +7818,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)) @@ -7820,6 +7832,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_DROWNNUMBERS: if (!P_DrownNumbersSceneryThink(mobj)) @@ -8658,7 +8673,7 @@ static boolean P_EggRobo1Think(mobj_t *mobj) < mobj->scale) S_StartSound(mobj, mobj->info->seesound); - P_TeleportMove(mobj, + P_MoveOrigin(mobj, (15*(mobj->x >> 4)) + (basex >> 4) + P_ReturnThrustX(mobj, mobj->angle, SPECTATORRADIUS >> 4), (15*(mobj->y >> 4)) + (basey >> 4) + P_ReturnThrustY(mobj, mobj->angle, SPECTATORRADIUS >> 4), mobj->z); @@ -8684,9 +8699,9 @@ static boolean P_EggRobo1Think(mobj_t *mobj) if (!didmove) { if (P_AproxDistance(mobj->x - basex, mobj->y - basey) < mobj->scale) - P_TeleportMove(mobj, basex, basey, mobj->z); + P_MoveOrigin(mobj, basex, basey, mobj->z); else - P_TeleportMove(mobj, + P_MoveOrigin(mobj, (15*(mobj->x >> 4)) + (basex >> 4), (15*(mobj->y >> 4)) + (basey >> 4), mobj->z); @@ -8808,11 +8823,11 @@ static void P_NiGHTSDroneThink(mobj_t *mobj) sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale); } - P_TeleportMove(goalpost, mobj->x, mobj->y, mobj->z + goaloffset); - P_TeleportMove(sparkle, mobj->x, mobj->y, mobj->z + sparkleoffset); + P_MoveOrigin(goalpost, mobj->x, mobj->y, mobj->z + goaloffset); + P_MoveOrigin(sparkle, mobj->x, mobj->y, mobj->z + sparkleoffset); if (goalpost->movefactor != mobj->z || goalpost->friction != mobj->height) { - P_TeleportMove(droneman, mobj->x, mobj->y, mobj->z + dronemanoffset); + P_MoveOrigin(droneman, mobj->x, mobj->y, mobj->z + dronemanoffset); goalpost->movefactor = mobj->z; goalpost->friction = mobj->height; } @@ -8822,12 +8837,12 @@ static void P_NiGHTSDroneThink(mobj_t *mobj) { if (goalpost->x != mobj->x || goalpost->y != mobj->y) { - P_TeleportMove(goalpost, mobj->x, mobj->y, goalpost->z); - P_TeleportMove(sparkle, mobj->x, mobj->y, sparkle->z); + P_MoveOrigin(goalpost, mobj->x, mobj->y, goalpost->z); + P_MoveOrigin(sparkle, mobj->x, mobj->y, sparkle->z); } if (droneman->x != mobj->x || droneman->y != mobj->y) - P_TeleportMove(droneman, mobj->x, mobj->y, + P_MoveOrigin(droneman, mobj->x, mobj->y, droneman->z >= mobj->floorz && droneman->z <= mobj->ceilingz ? droneman->z : mobj->z); } @@ -9026,7 +9041,7 @@ static void P_SaloonDoorThink(mobj_t *mobj) fma = (mobj->angle >> ANGLETOFINESHIFT) & FINEMASK; c = 48*FINECOSINE(fma); s = 48*FINESINE(fma); - P_TeleportMove(mobj, x + c0 + c, y + s0 + s, z); + P_MoveOrigin(mobj, x + c0 + c, y + s0 + s, z); } static void P_PyreFlyThink(mobj_t *mobj) @@ -9581,7 +9596,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) P_RemoveMobj(mobj); return false; } - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height); break; case MT_HAMMER: if (mobj->z <= mobj->floorz) @@ -10993,6 +11008,8 @@ 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); + R_AddMobjInterpolator(mobj); + return mobj; } @@ -11040,6 +11057,8 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype || mobj->subsector->sector->floorpic == skyflatnum) mobj->precipflags |= PCF_PIT; + R_ResetPrecipitationMobjInterpolationState(mobj); + return mobj; } @@ -11159,6 +11178,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! @@ -12562,7 +12583,7 @@ static boolean P_SetupNiGHTSDrone(mapthing_t *mthing, mobj_t *mobj) dronemangoaldiff = max(mobjinfo[MT_NIGHTSDRONE_MAN].height - mobjinfo[MT_NIGHTSDRONE_GOAL].height, 0); if (flip && mobj->height != oldheight) - P_TeleportMove(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight)); + P_MoveOrigin(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight)); if (!flip) { @@ -12631,9 +12652,9 @@ static boolean P_SetupNiGHTSDrone(mapthing_t *mthing, mobj_t *mobj) // correct Z position if (flip) { - P_TeleportMove(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset); - P_TeleportMove(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset); - P_TeleportMove(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset); + P_MoveOrigin(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset); + P_MoveOrigin(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset); + P_MoveOrigin(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset); } // Remember position preference for later @@ -14086,9 +14107,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 - elementheight; + + newmobj->old_z = mobj->old_z + mobj->height - zofs - elementheight; + newmobj->old_z2 = mobj->old_z2 + mobj->height - zofs - elementheight; + } + else + { + newmobj->old_z = mobj->old_z + zofs; + newmobj->old_z2 = mobj->old_z2 + zofs; } 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; + + // 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 da8d9ea2b6d9b18abdb720d32cd78037fd1ac6ca..60601692cf7f7b294f41f77eb25daea1880993d2 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -281,6 +281,8 @@ 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; @@ -288,6 +290,8 @@ 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 @@ -298,6 +302,8 @@ typedef struct mobj_s INT32 blendmode; // blend mode 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 @@ -373,6 +379,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; @@ -387,6 +395,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 fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius @@ -408,6 +417,8 @@ 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; @@ -415,6 +426,8 @@ 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 @@ -425,6 +438,8 @@ typedef struct precipmobj_s INT32 blendmode; // blend mode 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 77e514bee7a7a648069992d20160aef70cc7bddc..4062f9a9fcd361fcf6043843314d3e8041ba19cc 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" @@ -2049,6 +2050,9 @@ boolean EV_DoPolyObjRotate(polyrotdata_t *prdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + th->turnobjs = 0; if (!(prdata->flags & TMPR_DONTROTATEOTHERS)) th->turnobjs |= PTF_OTHERS; @@ -2114,6 +2118,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))) @@ -2129,8 +2136,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))) { @@ -2181,6 +2190,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; @@ -2229,6 +2258,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))) @@ -2269,6 +2301,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))) @@ -2340,6 +2375,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))) @@ -2386,6 +2424,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))) @@ -2490,6 +2531,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 07b3d8640ecdbc588f28d087463a2b5bc7ff2c27..c5195a17aea460c568b58a2eeb4e341e97373ccb 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" @@ -3060,6 +3061,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 21291eb8bbe0699de35382b9814fc654afecbb5c..90538de0b7f22c5a4f5c02e45dbf5879cb2aad2e 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_sound.h" // for I_PlayCD().. #include "i_video.h" // for I_FinishUpdate().. #include "r_sky.h" @@ -33,6 +34,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" @@ -7313,7 +7315,10 @@ static void P_RunSpecialStageWipe(void) { // wait loop while (!((nowtime = I_GetTime()) - lastwipetic)) - I_Sleep(); + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } lastwipetic = nowtime; if (moviemode) // make sure we save frames for the white hold too M_SaveFrame(); @@ -7615,7 +7620,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 6048f478d63f8ddc278df3f4d6b542c4de28ab42..8e00d58241d2ed4d4aeacaa387e539155609bcab 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" pslope_t *slopelist = NULL; UINT16 slopecount = 0; @@ -189,6 +190,9 @@ static inline void P_AddDynLineSlopeThinker (pslope_t* slope, dynplanetype_t typ th->sourceline = sourceline; th->extent = extent; P_AddThinker(THINK_DYNSLOPE, &th->thinker); + + // interpolation + R_CreateInterpolator_DynSlope(&th->thinker, slope); } static inline void P_AddDynVertexSlopeThinker (pslope_t* slope, const INT16 tags[3], const vector3_t vx[3]) diff --git a/src/p_spec.c b/src/p_spec.c index 72bc587142b2e574bc9262ddc83bec2ac795a8a9..9215b05e24ae40f0eeda789cdfc1194ce3f1efdf 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -21,6 +21,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" @@ -2480,7 +2481,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (mo->player) { if (bot) // This might put poor Tails in a wall if he's too far behind! D: But okay, whatever! >:3 - P_TeleportMove(bot, bot->x + x, bot->y + y, bot->z + z); + P_SetOrigin(bot, bot->x + x, bot->y + y, bot->z + z); if (splitscreen && mo->player == &players[secondarydisplayplayer] && camera2.chase) { camera2.x += x; @@ -2726,7 +2727,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // Reset bot too. if (bot) { if (line->args[0]) - P_TeleportMove(bot, mo->x, mo->y, mo->z); + P_SetOrigin(bot, mo->x, mo->y, mo->z); bot->momx = bot->momy = bot->momz = 1; bot->pmomz = 0; bot->player->rmomx = bot->player->rmomy = 1; @@ -2767,7 +2768,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // (Teleport them to you so they don't break it.) if (bot && (bot->flags2 & MF2_TWOD) != (mo->flags2 & MF2_TWOD)) { bot->flags2 = (bot->flags2 & ~MF2_TWOD) | (mo->flags2 & MF2_TWOD); - P_TeleportMove(bot, mo->x, mo->y, mo->z); + P_SetOrigin(bot, mo->x, mo->y, mo->z); } } break; @@ -5703,6 +5704,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); } /** @@ -5732,6 +5737,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 @@ -5791,6 +5799,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) @@ -5816,6 +5828,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. @@ -5856,6 +5872,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. @@ -7524,6 +7544,22 @@ static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT3 } } 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; + } } static void P_SpawnPlaneScroller(line_t *l, fixed_t dx, fixed_t dy, INT32 control, INT32 affectee, INT32 accel, INT32 exclusive) diff --git a/src/p_telept.c b/src/p_telept.c index cbbd0ff6bcffc7b91db3a5d6c7323d55394a4b4c..4d10f1df30a807908c32c21629a275c334a8c851 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -122,7 +122,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, */ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, boolean flash, boolean dontstopmove) { - if (!P_TeleportMove(thing, x, y, z)) + if (!P_SetOrigin(thing, x, y, z)) return false; if (!dontstopmove) diff --git a/src/p_tick.c b/src/p_tick.c index 28ace92883a6c9d5078e1b1fe30098b07536639a..bf6e8f42ed1246b4067d626bbefe048b66b68baf 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -23,6 +23,9 @@ #include "lua_hook.h" #include "m_perfstats.h" #include "i_system.h" // I_GetPreciseTime +#include "r_main.h" +#include "r_fps.h" +#include "i_video.h" // rendermode // Object place #include "m_cheat.h" @@ -254,6 +257,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); } @@ -616,8 +620,10 @@ void P_Ticker(boolean run) if (OP_FreezeObjectplace()) { P_MapStart(); + R_UpdateMobjInterpolators(); OP_ObjectplaceMovement(&players[0]); P_MoveChaseCamera(&players[0], &camera, false); + R_UpdateViewInterpolation(); P_MapEnd(); S_SetStackAdjustmentStart(); return; @@ -640,6 +646,8 @@ void P_Ticker(boolean run) if (run) { + R_UpdateMobjInterpolators(); + if (demorecording) G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0); if (demoplayback) @@ -763,6 +771,42 @@ void P_Ticker(boolean run) LUA_HOOK(PostThinkFrame); } + if (run) + { + R_UpdateLevelInterpolators(); + R_UpdateViewInterpolation(); + + // Hack: ensure newview is assigned every tic. + // Ensures view interpolation is T-1 to T in poor network conditions + // We need a better way to assign view state decoupled from game logic + if (rendermode != render_none) + { + player_t *player1 = &players[displayplayer]; + if (player1->mo && skyboxmo[0] && cv_skybox.value) + { + R_SkyboxFrame(player1); + } + if (player1->mo) + { + R_SetupFrame(player1); + } + + if (splitscreen) + { + player_t *player2 = &players[secondarydisplayplayer]; + if (player2->mo && skyboxmo[0] && cv_skybox.value) + { + R_SkyboxFrame(player2); + } + if (player2->mo) + { + R_SetupFrame(player2); + } + } + } + + } + P_MapEnd(); // Z_CheckMemCleanup(); @@ -783,6 +827,8 @@ void P_PreTicker(INT32 frames) { P_MapStart(); + R_UpdateMobjInterpolators(); + LUA_HOOK(PreThinkFrame); for (i = 0; i < MAXPLAYERS; i++) @@ -821,6 +867,10 @@ void P_PreTicker(INT32 frames) LUA_HOOK(PostThinkFrame); + R_UpdateLevelInterpolators(); + R_UpdateViewInterpolation(); + R_ResetViewInterpolation(0); + P_MapEnd(); } diff --git a/src/p_user.c b/src/p_user.c index 2f522ad4b1eb8c33d753184da0bcb2fe8e327937..49f21494126f1a704fb6eb32c52dc520e8504c91 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" @@ -1996,6 +1997,14 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW); } + // Copy interpolation data :) + 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; } @@ -6931,7 +6940,7 @@ static void P_MoveNiGHTSToDrone(player_t *player) } player->mo->momx = player->mo->momy = player->mo->momz = 0; - P_TeleportMove(player->mo, player->drone->x, player->drone->y, player->drone->z + zofs); + P_MoveOrigin(player->mo, player->drone->x, player->drone->y, player->drone->z + zofs); P_SetTarget(&player->drone, NULL); } @@ -9647,6 +9656,7 @@ void P_ResetCamera(player_t *player, camera_t *thiscam) thiscam->x = x; thiscam->y = y; thiscam->z = z; + thiscam->reset = true; if ((thiscam == &camera && G_ControlStyle(1) == CS_SIMPLE) || (thiscam == &camera2 && G_ControlStyle(2) == CS_SIMPLE)) @@ -10997,7 +11007,7 @@ static void P_MinecartThink(player_t *player) } // Move player to minecart. - P_TeleportMove(player->mo, minecart->x - minecart->momx, minecart->y - minecart->momy, minecart->z + max(minecart->momz, 0) + 8*FRACUNIT); + P_MoveOrigin(player->mo, minecart->x - minecart->momx, minecart->y - minecart->momy, minecart->z + max(minecart->momz, 0) + 8*FRACUNIT); if (player->powers[pw_carry] != CR_MINECART) return; player->mo->momx = player->mo->momy = player->mo->momz = 0; @@ -11236,6 +11246,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume) tic_t dashmode = min(player->dashmode, DASHMODE_MAX); boolean underwater = mo->eflags & MFE_UNDERWATER; statenum_t stat = fume->state-states; + boolean resetinterp = false; if (panim != PA_WALK && panim != PA_RUN && panim != PA_DASH) // turn invisible when not in a coherent movement state { @@ -11287,6 +11298,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume) { P_SetMobjState(fume, (stat = fume->info->seestate)); P_SetScale(fume, mo->scale); + resetinterp = true; } if (dashmode > DASHMODE_THRESHOLD && stat != fume->info->seestate) // If in dashmode, grow really big and flash @@ -11330,6 +11342,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume) fume->y = mo->y + P_ReturnThrustY(fume, angle, dist); fume->z = mo->z + heightoffset - (fume->height >> 1); P_SetThingPosition(fume); + if (resetinterp) R_ResetMobjInterpolationState(fume); // If dashmode is high enough, spawn a trail if (player->normalspeed >= skins[player->skin].normalspeed*2) @@ -11732,7 +11745,7 @@ void P_PlayerThink(player_t *player) // P_TouchSpecialThing case MT_NIGHTSBUMPER, that position is fudged in the time // between that routine in the previous tic // and reaching here in the current tic - P_TeleportMove(player->mo, player->mo->hnext->x, player->mo->hnext->y + P_MoveOrigin(player->mo, player->mo->hnext->x, player->mo->hnext->y , player->mo->hnext->z + FixedMul(player->mo->hnext->height/4, player->mo->hnext->scale)); P_SetTarget(&player->mo->hnext, NULL); } @@ -12556,7 +12569,7 @@ void P_PlayerAfterThink(player_t *player) player->mo->momx = (chain->x - player->mo->x)*2; player->mo->momy = (chain->y - player->mo->y)*2; player->mo->momz = (chain->z - (player->mo->height-chain->height/2) - player->mo->z)*2; - P_TeleportMove(player->mo, chain->x, chain->y, chain->z - (player->mo->height-chain->height/2)); + P_MoveOrigin(player->mo, chain->x, chain->y, chain->z - (player->mo->height-chain->height/2)); if (!player->powers[pw_flashing]) // handle getting hurt { player->pflags |= PF_JUMPED; @@ -12650,7 +12663,7 @@ void P_PlayerAfterThink(player_t *player) mo->tics = walktics; } - P_TeleportMove(player->mo, rock->x, rock->y, rock->z + ((mo->eflags & MFE_VERTICALFLIP) ? -mo->height : rock->height)); + P_MoveOrigin(player->mo, rock->x, rock->y, rock->z + ((mo->eflags & MFE_VERTICALFLIP) ? -mo->height : rock->height)); break; } case CR_PTERABYTE: // being carried by a Pterabyte diff --git a/src/r_bsp.c b/src/r_bsp.c index c9591b254469a2e512841c781aecb2d77c298118..bf238a4357991bdd7eaeb533a2531e6550c2908b 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -840,6 +840,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) @@ -866,7 +867,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); @@ -914,7 +931,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 new file mode 100644 index 0000000000000000000000000000000000000000..f3675ab97ef2e1a57677da60c08582c9f5f835da --- /dev/null +++ b/src/r_fps.c @@ -0,0 +1,817 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2000 by Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko (prboom) +// Copyright (C) 1999-2019 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_fps.h +/// \brief Uncapped framerate stuff. + +#include "r_fps.h" + +#include "r_main.h" +#include "g_game.h" +#include "i_video.h" +#include "r_plane.h" +#include "p_spec.h" +#include "r_state.h" +#include "z_zone.h" +#include "console.h" // con_startup_loadprogress +#include "m_perfstats.h" // ps_metric_t +#ifdef HWRENDER +#include "hardware/hw_main.h" // for cv_glshearing +#endif + +static CV_PossibleValue_t fpscap_cons_t[] = { +#ifdef DEVELOP + // Lower values are actually pretty useful for debugging interp problems! + {1, "MIN"}, +#else + {TICRATE, "MIN"}, +#endif + {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); + +ps_metric_t ps_interp_frac = {0}; +ps_metric_t ps_interp_lag = {0}; + +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) + { + // -1: Unlimited + return 0; + } + + return cv_fpscap.value; +} + +boolean R_UsingFrameInterpolation(void) +{ + return (R_GetFramerateCap() != TICRATE || cv_timescale.value < FRACUNIT); +} + +static viewvars_t p1view_old; +static viewvars_t p1view_new; +static viewvars_t p2view_old; +static viewvars_t p2view_new; +static viewvars_t sky1view_old; +static viewvars_t sky1view_new; +static viewvars_t sky2view_old; +static viewvars_t sky2view_new; + +static viewvars_t *oldview = &p1view_old; +static int oldview_invalid[MAXSPLITSCREENPLAYERS] = {0, 0}; +viewvars_t *newview = &p1view_new; + + +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 from + FixedMul(frac, to - from); +} + +static angle_t R_LerpAngle(angle_t from, angle_t to, fixed_t frac) +{ + 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 +// slopes are already calculated for the full possible view (which is 4*viewheight). +// 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) +static void R_SetupFreelook(player_t *player, boolean skybox) +{ +#ifndef HWRENDER + (void)player; + (void)skybox; +#endif + + // clip it in the case we are looking a hardware 90 degrees full aiming + // (lmps, network and use F12...) + if (rendermode == render_soft +#ifdef HWRENDER + || (rendermode == render_opengl + && (cv_glshearing.value == 1 + || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) +#endif + ) + { + G_SoftwareClipAimingPitch((INT32 *)&aimingangle); + } + + centeryfrac = (viewheight/2)<<FRACBITS; + + if (rendermode == render_soft) + centeryfrac += FixedMul(AIMINGTODY(aimingangle), FixedDiv(viewwidth<<FRACBITS, BASEVIDWIDTH<<FRACBITS)); + + centery = FixedInt(FixedRound(centeryfrac)); + + if (rendermode == render_soft) + yslope = &yslopetab[viewheight*8 - centery]; +} + +#undef AIMINGTODY + +void R_InterpolateView(fixed_t frac) +{ + viewvars_t* prevview = oldview; + boolean skybox = 0; + UINT8 i; + + if (FIXED_TO_FLOAT(frac) < 0) + frac = 0; + if (frac > FRACUNIT) + frac = FRACUNIT; + + if (viewcontext == VIEWCONTEXT_SKY1 || viewcontext == VIEWCONTEXT_PLAYER1) + { + i = 0; + } + else + { + i = 1; + } + + 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); + + viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); + viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + + // this is gonna create some interesting visual errors for long distance teleports... + // might want to recalculate the view sector every frame instead... + viewplayer = newview->player; + viewsector = R_PointInSubsector(viewx, viewy)->sector; + + // well, this ain't pretty + if (newview == &sky1view_new || newview == &sky2view_new) + { + skybox = 1; + } + + R_SetupFreelook(newview->player, skybox); +} + +void R_UpdateViewInterpolation(void) +{ + p1view_old = p1view_new; + p2view_old = p2view_new; + sky1view_old = sky1view_new; + sky2view_old = sky2view_new; + if (oldview_invalid[0] > 0) oldview_invalid[0]--; + if (oldview_invalid[1] > 0) oldview_invalid[1]--; +} + +void R_ResetViewInterpolation(UINT8 p) +{ + if (p == 0) + { + UINT8 i; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + oldview_invalid[i]++; + } + } + else + { + oldview_invalid[p - 1]++; + } +} + +void R_SetViewContext(enum viewcontext_e _viewcontext) +{ + I_Assert(_viewcontext == VIEWCONTEXT_PLAYER1 + || _viewcontext == VIEWCONTEXT_PLAYER2 + || _viewcontext == VIEWCONTEXT_SKY1 + || _viewcontext == VIEWCONTEXT_SKY2); + viewcontext = _viewcontext; + + switch (viewcontext) + { + case VIEWCONTEXT_PLAYER1: + oldview = &p1view_old; + newview = &p1view_new; + break; + case VIEWCONTEXT_PLAYER2: + oldview = &p2view_old; + newview = &p2view_new; + break; + case VIEWCONTEXT_SKY1: + oldview = &sky1view_old; + newview = &sky1view_new; + break; + case VIEWCONTEXT_SKY2: + oldview = &sky2view_old; + newview = &sky2view_new; + break; + default: + I_Error("viewcontext value is invalid: we should never get here without an assert!!"); + break; + } +} + +fixed_t R_InterpolateFixed(fixed_t from, fixed_t to) +{ + if (!R_UsingFrameInterpolation()) + { + return to; + } + + return (R_LerpFixed(from, to, rendertimefrac)); +} + +angle_t R_InterpolateAngle(angle_t from, angle_t to) +{ + if (!R_UsingFrameInterpolation()) + { + return to; + } + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..85c87a2f49ff1c3177f7fb8b8e9136e88316ebc9 --- /dev/null +++ b/src/r_fps.h @@ -0,0 +1,162 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2000 by Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko (prboom) +// Copyright (C) 1999-2019 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_fps.h +/// \brief Uncapped framerate stuff. + +#ifndef __R_FPS_H__ +#define __R_FPS_H__ + +#include "m_fixed.h" +#include "p_local.h" +#include "r_state.h" +#include "m_perfstats.h" // ps_metric_t + +extern consvar_t cv_fpscap; + +extern ps_metric_t ps_interp_frac; +extern ps_metric_t ps_interp_lag; + +UINT32 R_GetFramerateCap(void); +boolean R_UsingFrameInterpolation(void); + +enum viewcontext_e +{ + VIEWCONTEXT_PLAYER1 = 0, + VIEWCONTEXT_PLAYER2, + VIEWCONTEXT_SKY1, + VIEWCONTEXT_SKY2 +}; + +typedef struct { + fixed_t x; + fixed_t y; + fixed_t z; + boolean sky; + sector_t *sector; + player_t *player; + + angle_t angle; + angle_t aim; + fixed_t cos; + fixed_t sin; + mobj_t *mobj; +} viewvars_t; + +extern viewvars_t *newview; + +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); +// 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 f19962d412667646804fc72c6ad9f5535fb20315..cd5e255ed4059ef0680e782298284f422dc27611 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -35,6 +35,7 @@ #include "r_portal.h" #include "r_main.h" #include "i_system.h" // I_GetPreciseTime +#include "r_fps.h" // Frame interpolation/uncapped #ifdef HWRENDER #include "hardware/hw_main.h" @@ -75,6 +76,10 @@ sector_t *viewsector; player_t *viewplayer; mobj_t *r_viewmobj; +fixed_t rendertimefrac; +fixed_t renderdeltatics; +boolean renderisnewtic; + // // precalculated math tables // @@ -1084,41 +1089,6 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y) // // R_SetupFrame // - -// recalc necessary stuff for mouseaiming -// slopes are already calculated for the full possible view (which is 4*viewheight). -// 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) -static void R_SetupFreelook(player_t *player, boolean skybox) -{ -#ifndef HWRENDER - (void)player; - (void)skybox; -#endif - - // clip it in the case we are looking a hardware 90 degrees full aiming - // (lmps, network and use F12...) - if (rendermode == render_soft -#ifdef HWRENDER - || (rendermode == render_opengl - && (cv_glshearing.value == 1 - || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) -#endif - ) - { - G_SoftwareClipAimingPitch((INT32 *)&aimingangle); - } - - centeryfrac = (viewheight/2)<<FRACBITS; - - if (rendermode == render_soft) - centeryfrac += FixedMul(AIMINGTODY(aimingangle), FixedDiv(viewwidth<<FRACBITS, BASEVIDWIDTH<<FRACBITS)); - - centery = FixedInt(FixedRound(centeryfrac)); - - if (rendermode == render_soft) - yslope = &yslopetab[viewheight*8 - centery]; -} - void R_SetupFrame(player_t *player) { camera_t *thiscam; @@ -1129,11 +1099,13 @@ void R_SetupFrame(player_t *player) { thiscam = &camera2; chasecam = (cv_chasecam2.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER2); } else { thiscam = &camera; chasecam = (cv_chasecam.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER1); } if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode) @@ -1149,81 +1121,83 @@ void R_SetupFrame(player_t *player) else if (!chasecam) thiscam->chase = false; + newview->sky = false; + if (player->awayviewtics) { // cut-away view stuff r_viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN I_Assert(r_viewmobj != NULL); - viewz = r_viewmobj->z + 20*FRACUNIT; - aimingangle = player->awayviewaiming; - viewangle = r_viewmobj->angle; + newview->z = r_viewmobj->z + 20*FRACUNIT; + newview->aim = player->awayviewaiming; + newview->angle = r_viewmobj->angle; } else if (!player->spectator && chasecam) // use outside cam view { r_viewmobj = NULL; - viewz = thiscam->z + (thiscam->height>>1); - aimingangle = thiscam->aiming; - viewangle = thiscam->angle; + newview->z = thiscam->z + (thiscam->height>>1); + newview->aim = thiscam->aiming; + newview->angle = thiscam->angle; } else // use the player's eyes view { - viewz = player->viewz; + newview->z = player->viewz; r_viewmobj = player->mo; I_Assert(r_viewmobj != NULL); - aimingangle = player->aiming; - viewangle = r_viewmobj->angle; + newview->aim = player->aiming; + newview->angle = r_viewmobj->angle; if (!demoplayback && player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle; // WARNING: camera uses this - aimingangle = localaiming; + newview->angle = localangle; // WARNING: camera uses this + newview->aim = localaiming; } else if (player == &players[secondarydisplayplayer]) { - viewangle = localangle2; - aimingangle = localaiming2; + newview->angle = localangle2; + newview->aim = localaiming2; } } } - viewz += quake.z; + newview->z += quake.z; - viewplayer = player; + newview->player = player; if (chasecam && !player->awayviewtics && !player->spectator) { - viewx = thiscam->x; - viewy = thiscam->y; - viewx += quake.x; - viewy += quake.y; + newview->x = thiscam->x; + newview->y = thiscam->y; + newview->x += quake.x; + newview->y += quake.y; if (thiscam->subsector) - viewsector = thiscam->subsector->sector; + newview->sector = thiscam->subsector->sector; else - viewsector = R_PointInSubsector(viewx, viewy)->sector; + newview->sector = R_PointInSubsector(newview->x, newview->y)->sector; } else { - viewx = r_viewmobj->x; - viewy = r_viewmobj->y; - viewx += quake.x; - viewy += quake.y; + newview->x = r_viewmobj->x; + newview->y = r_viewmobj->y; + newview->x += quake.x; + newview->y += quake.y; if (r_viewmobj->subsector) - viewsector = r_viewmobj->subsector->sector; + newview->sector = r_viewmobj->subsector->sector; else - viewsector = R_PointInSubsector(viewx, viewy)->sector; + newview->sector = R_PointInSubsector(newview->x, newview->y)->sector; } - viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); - viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + // newview->sin = FINESINE(viewangle>>ANGLETOFINESHIFT); + // newview->cos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(player, false); + R_InterpolateView(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); } void R_SkyboxFrame(player_t *player) @@ -1232,11 +1206,18 @@ void R_SkyboxFrame(player_t *player) if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer]) + { thiscam = &camera2; + R_SetViewContext(VIEWCONTEXT_SKY2); + } else + { thiscam = &camera; + R_SetViewContext(VIEWCONTEXT_SKY1); + } // cut-away view stuff + newview->sky = true; r_viewmobj = skyboxmo[0]; #ifdef PARANOIA if (!r_viewmobj) @@ -1247,39 +1228,39 @@ void R_SkyboxFrame(player_t *player) #endif if (player->awayviewtics) { - aimingangle = player->awayviewaiming; - viewangle = player->awayviewmobj->angle; + newview->aim = player->awayviewaiming; + newview->angle = player->awayviewmobj->angle; } else if (thiscam->chase) { - aimingangle = thiscam->aiming; - viewangle = thiscam->angle; + newview->aim = thiscam->aiming; + newview->angle = thiscam->angle; } else { - aimingangle = player->aiming; - viewangle = player->mo->angle; + newview->aim = player->aiming; + newview->angle = player->mo->angle; if (!demoplayback && player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle; // WARNING: camera uses this - aimingangle = localaiming; + newview->angle = localangle; // WARNING: camera uses this + newview->aim = localaiming; } else if (player == &players[secondarydisplayplayer]) { - viewangle = localangle2; - aimingangle = localaiming2; + newview->angle = localangle2; + newview->aim = localaiming2; } } } - viewangle += r_viewmobj->angle; + newview->angle += r_viewmobj->angle; - viewplayer = player; + newview->player = player; - viewx = r_viewmobj->x; - viewy = r_viewmobj->y; - viewz = r_viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle! + newview->x = r_viewmobj->x; + newview->y = r_viewmobj->y; + newview->z = r_viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle! if (mapheaderinfo[gamemap-1]) { @@ -1321,56 +1302,75 @@ void R_SkyboxFrame(player_t *player) if (r_viewmobj->angle == 0) { - viewx += x; - viewy += y; + newview->x += x; + newview->y += y; } else if (r_viewmobj->angle == ANGLE_90) { - viewx -= y; - viewy += x; + newview->x -= y; + newview->y += x; } else if (r_viewmobj->angle == ANGLE_180) { - viewx -= x; - viewy -= y; + newview->x -= x; + newview->y -= y; } else if (r_viewmobj->angle == ANGLE_270) { - viewx += y; - viewy -= x; + newview->x += y; + newview->y -= x; } else { angle_t ang = r_viewmobj->angle>>ANGLETOFINESHIFT; - viewx += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang)); - viewy += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang)); + newview->x += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang)); + newview->y += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang)); } } if (mh->skybox_scalez > 0) - viewz += campos.z / mh->skybox_scalez; + newview->z += campos.z / mh->skybox_scalez; else if (mh->skybox_scalez < 0) - viewz += campos.z * -mh->skybox_scalez; + newview->z += campos.z * -mh->skybox_scalez; } if (r_viewmobj->subsector) - viewsector = r_viewmobj->subsector->sector; + newview->sector = r_viewmobj->subsector->sector; else - viewsector = R_PointInSubsector(viewx, viewy)->sector; + newview->sector = R_PointInSubsector(newview->x, newview->y)->sector; - viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); - viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + // newview->sin = FINESINE(viewangle>>ANGLETOFINESHIFT); + // newview->cos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); - R_SetupFreelook(player, true); + R_InterpolateView(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); } boolean R_ViewpointHasChasecam(player_t *player) { + camera_t *thiscam; boolean chasecam = false; if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer]) + { + thiscam = &camera2; chasecam = (cv_chasecam2.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER2); + if (thiscam->reset) + { + R_ResetViewInterpolation(2); + thiscam->reset = false; + } + } else + { + thiscam = &camera; chasecam = (cv_chasecam.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER1); + if (thiscam->reset) + { + R_ResetViewInterpolation(1); + thiscam->reset = false; + } + } if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode) chasecam = true; // force chasecam on @@ -1642,4 +1642,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_maxportals); CV_RegisterVar(&cv_movebob); + + // Frame interpolation/uncapped + CV_RegisterVar(&cv_fpscap); } diff --git a/src/r_main.h b/src/r_main.h index c0edb31b30175295ecbf9fba247ef49a699953b9..94103ceed9ee5017b7f08831bd17a833c23f9d4b 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -35,6 +35,13 @@ extern fixed_t fovtan; extern size_t validcount, linecount, loopcount, framecount; +// The fraction of a tic being drawn (for interpolation between two tics) +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. // Used for z-depth cuing per column/row, diff --git a/src/r_things.c b/src/r_things.c index e689e284a96b1e90a9bc68b5d0260f4061990516..82292957475b494e9191b08911f3ed5601b094fb 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" @@ -1134,13 +1135,33 @@ static void R_SplitSprite(vissprite_t *sprite) // fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) { + fixed_t halfHeight; boolean isflipped = thing->eflags & MFE_VERTICALFLIP; + 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; -#define CHECKZ (isflipped ? z > thing->z+thing->height/2 && z < groundz : z < thing->z+thing->height/2 && z > groundz) + + // 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) { @@ -1151,7 +1172,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) if (sector->heightsec != -1) z = isflipped ? sectors[sector->heightsec].ceilingheight : sectors[sector->heightsec].floorheight; else - z = isflipped ? P_GetSectorCeilingZAt(sector, thing->x, thing->y) : P_GetSectorFloorZAt(sector, thing->x, thing->y); + z = isflipped ? P_GetSectorCeilingZAt(sector, interp.x, interp.y) : P_GetSectorFloorZAt(sector, interp.x, interp.y); if CHECKZ { @@ -1165,7 +1186,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) if (!(rover->fofflags & FOF_EXISTS) || !(rover->fofflags & FOF_RENDERPLANES) || (rover->alpha < 90 && !(rover->fofflags & FOF_SWIMMABLE))) continue; - z = isflipped ? P_GetFFloorBottomZAt(rover, thing->x, thing->y) : P_GetFFloorTopZAt(rover, thing->x, thing->y); + z = isflipped ? P_GetFFloorBottomZAt(rover, interp.x, interp.y) : P_GetFFloorTopZAt(rover, interp.x, interp.y); if CHECKZ { groundz = z; @@ -1174,71 +1195,38 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) } } - if (isflipped ? (thing->ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))) - : (thing->floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))) + // Check polyobjects and see if groundz needs to be altered { - groundz = isflipped ? thing->ceilingz : thing->floorz; - groundslope = NULL; - } + // This isn't very precise, but the precise method was far too slow. + // (Polies are just naturally pretty flickery anyway :P) + polyobj_t *po = interp.subsector->polyList; -#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)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - validcount++; - - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) + while (po) + { + if (!(po->flags & POF_RENDERPLANES) || !P_MobjInsidePolyobj(po, thing)) { - 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; + po = (polyobj_t *)(po->link.next); + continue; + } - if (!P_MobjInsidePolyobj(po, thing) || !(po->flags & POF_RENDERPLANES)) - { - plink = (polymaplink_t *)(plink->link.next); - continue; - } + // We're inside it! Yess... + z = isflipped ? po->lines[0]->backsector->floorheight : po->lines[0]->backsector->ceilingheight; + if CHECKZ + { + groundz = z; + groundslope = NULL; + } - // We're inside it! Yess... - z = po->lines[0]->backsector->ceilingheight; + po = (polyobj_t *)(po->link.next); + } + } - if (z < thing->z+thing->height/2 && z > groundz) - { - groundz = z; - groundslope = NULL; - } - } - plink = (polymaplink_t *)(plink->link.next); - } - } + if (isflipped ? (ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))) + : (floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))) + { + groundz = isflipped ? ceilingz : floorz; + groundslope = NULL; } -#endif if (shadowslope != NULL) *shadowslope = groundslope; @@ -1252,9 +1240,24 @@ static void R_SkewShadowSprite( fixed_t groundz, INT32 spriteheight, fixed_t scalemul, fixed_t *shadowyscale, fixed_t *shadowskew) { + // haha let's try some dumb stuff fixed_t xslope, zslope; - angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - 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); @@ -1282,11 +1285,21 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t groundz; pslope_t *groundslope; boolean isflipped = thing->eflags & MFE_VERTICALFLIP; + interpmobjstate_t interp = {0}; 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); + } + heightsec = thing->subsector->sector->heightsec; if (viewplayer->mo && viewplayer->mo->subsector) phs = viewplayer->mo->subsector->sector->heightsec; @@ -1305,7 +1318,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, return; } - floordiff = abs((isflipped ? thing->height : 0) + thing->z - groundz); + floordiff = abs((isflipped ? thing->height : 0) + interp.z - groundz); trans = floordiff / (100*FRACUNIT) + 3; if (trans >= 9) return; @@ -1345,11 +1358,11 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->mobjflags = 0; shadow->sortscale = vis->sortscale; shadow->dispoffset = vis->dispoffset - 5; - shadow->gx = thing->x; - shadow->gy = thing->y; + shadow->gx = interp.x; + shadow->gy = interp.y; shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + 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; @@ -1363,7 +1376,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, 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); @@ -1391,7 +1404,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, // 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; @@ -1469,7 +1482,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 @@ -1481,9 +1494,24 @@ static void R_ProjectSprite(mobj_t *thing) INT32 rollangle = 0; #endif + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(oldthing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(oldthing, FRACUNIT, &interp); + } + + this_scale = interp.scale; + // transform the origin point - tr_x = thing->x - viewx; - tr_y = thing->y - viewy; + tr_x = interp.x - viewx; + tr_y = interp.y - viewy; basetz = tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance @@ -1560,7 +1588,7 @@ static void R_ProjectSprite(mobj_t *thing) if (sprframe->rotate != SRF_SINGLE || papersprite) { - ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle); + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if (mirrored) ang = InvAngle(ang); } @@ -1575,7 +1603,7 @@ static void R_ProjectSprite(mobj_t *thing) else { // choose a different rotation based on player view - //ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + //ang = R_PointToAngle (interp.x, interp.y) - interpangle; if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right rot = 6; // F7 slot @@ -1644,15 +1672,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 { @@ -1661,8 +1689,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) @@ -1684,8 +1712,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); @@ -1701,7 +1729,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); @@ -1789,6 +1817,7 @@ 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; @@ -1796,8 +1825,17 @@ static void R_ProjectSprite(mobj_t *thing) if (! R_ThingVisible(thing)) return; - tr_x = (thing->x + sort_x) - viewx; - tr_y = (thing->y + sort_y) - viewy; + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &tracer_interp); + } + else + { + R_InterpolateMobjState(thing, FRACUNIT, &tracer_interp); + } + + 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, tz); @@ -1812,8 +1850,8 @@ static void R_ProjectSprite(mobj_t *thing) } else 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); sortscale = FixedDiv(projectiony, sort_z); } @@ -1821,8 +1859,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, sort_z); } @@ -1833,7 +1871,7 @@ static void R_ProjectSprite(mobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; - if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0) + if (P_PointOnLineSide(interp.x, interp.y, portalclipline) != 0) return; } @@ -1868,6 +1906,16 @@ static void R_ProjectSprite(mobj_t *thing) if (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)) { @@ -1876,9 +1924,9 @@ static void R_ProjectSprite(mobj_t *thing) if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes - floordiff = abs((isflipped ? caster->height : 0) + caster->z - groundz); + floordiff = abs((isflipped ? caster->height : 0) + casterinterp.z - groundz); trans += ((floordiff / (100*FRACUNIT)) + 3); - shadowscale = FixedMul(FRACUNIT - floordiff/640, caster->scale); + shadowscale = FixedMul(FRACUNIT - floordiff/640, casterinterp.scale); } else trans += 3; @@ -1907,7 +1955,7 @@ static void R_ProjectSprite(mobj_t *thing) { 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; @@ -1922,12 +1970,12 @@ static void R_ProjectSprite(mobj_t *thing) // When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned. // sprite height - sprite topoffset is the proper inverse of the vertical offset, of course. // remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes! - gz = oldthing->z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); + gz = interp.z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale)); } else { - gzt = oldthing->z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); + gzt = interp.z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale)); gz = gzt - FixedMul(spr_height, FixedMul(spriteyscale, this_scale)); } } @@ -1946,7 +1994,7 @@ static void R_ProjectSprite(mobj_t *thing) // R_GetPlaneLight won't work on sloped lights! for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { - fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], thing->x, thing->y); + fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interp.x, interp.y); if (h <= top) { light = lightnum - 1; break; @@ -1972,7 +2020,7 @@ static void R_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { fixed_t top = gzt; - fixed_t bottom = thing->z; + fixed_t bottom = interp.z; if (splat) top = bottom; @@ -1996,12 +2044,12 @@ static void R_ProjectSprite(mobj_t *thing) vis->sortscale = sortscale; vis->sortsplat = sortsplat; vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15 - vis->gx = thing->x; - vis->gy = thing->y; + vis->gx = interp.x; + vis->gy = interp.y; vis->gz = gz; vis->gzt = gzt; vis->thingheight = thing->height; - vis->pz = thing->z; + vis->pz = interp.z; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = FixedDiv(gzt - viewz, spriteyscale); vis->scalestep = scalestep; @@ -2036,7 +2084,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000 vis->scale = FixedMul(spriteyscale, yscale); //<<detailshift; - vis->thingscale = oldthing->scale; + vis->thingscale = interp.scale; vis->spritexscale = spritexscale; vis->spriteyscale = spriteyscale; @@ -2144,9 +2192,22 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) //SoM: 3/17/2000 fixed_t gz, gzt; + // 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 = thing->x - viewx; - tr_y = thing->y - viewy; + tr_x = interp.x - viewx; + tr_y = interp.y - viewy; tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance @@ -2210,13 +2271,13 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; - if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0) + if (P_PointOnLineSide(interp.x, interp.y, portalclipline) != 0) return; } //SoM: 3/17/2000: Disregard sprites that are out of view.. - gzt = thing->z + spritecachedinfo[lump].topoffset; + gzt = interp.z + spritecachedinfo[lump].topoffset; gz = gzt - spritecachedinfo[lump].height; if (thing->subsector->sector->cullheight) @@ -2229,12 +2290,12 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis = R_NewVisSprite(); vis->scale = vis->sortscale = yscale; //<<detailshift; vis->dispoffset = 0; // Monster Iestyn: 23/11/15 - vis->gx = thing->x; - vis->gy = thing->y; + vis->gx = interp.x; + vis->gy = interp.y; vis->gz = gz; vis->gzt = gzt; vis->thingheight = 4*FRACUNIT; - vis->pz = thing->z; + vis->pz = interp.z; vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = 0; diff --git a/src/screen.c b/src/screen.c index 73af4313deab86ec5bdf4418f8428d6b035a4e70..ef02a477154ca3cf85969cc0881835438487a4a2 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" @@ -33,12 +34,15 @@ #include "s_sound.h" // ditto #include "g_game.h" // ditto #include "p_local.h" // P_AutoPause() + #ifdef HWRENDER #include "hardware/hw_main.h" #include "hardware/hw_light.h" #include "hardware/hw_model.h" #endif +// SRB2Kart +#include "r_fps.h" // R_GetFramerateCap #if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200)) #define RUSEASM //MSC.NET can't patch itself @@ -67,6 +71,7 @@ static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, consvar_t cv_scr_width = CVAR_INIT ("scr_width", "1280", CV_SAVE, CV_Unsigned, NULL); consvar_t cv_scr_height = CVAR_INIT ("scr_height", "800", CV_SAVE, CV_Unsigned, NULL); consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL); + consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL); CV_PossibleValue_t cv_renderer_t[] = { @@ -447,46 +452,117 @@ boolean SCR_IsAspectCorrect(INT32 width, INT32 height) ); } -// XMOD FPS display -// moved out of os-specific code for consistency -static boolean fpsgraph[TICRATE]; -static tic_t lasttic; +double averageFPS = 0.0f; + +#define USE_FPS_SAMPLES + +#ifdef USE_FPS_SAMPLES +#define FPS_SAMPLE_RATE (0.05) // How often to update FPS samples, in seconds +#define NUM_FPS_SAMPLES (16) // Number of samples to store + +static double fps_samples[NUM_FPS_SAMPLES]; +static double updateElapsed = 0.0; +#endif + +static boolean fps_init = false; +static precise_t fps_enter = 0; + +void SCR_CalculateFPS(void) +{ + precise_t fps_finish = 0; + + double frameElapsed = 0.0; + + if (fps_init == false) + { + fps_enter = I_GetPreciseTime(); + fps_init = true; + } + + fps_finish = I_GetPreciseTime(); + frameElapsed = (double)((INT64)(fps_finish - fps_enter)) / I_GetPrecisePrecision(); + fps_enter = fps_finish; + +#ifdef USE_FPS_SAMPLES + updateElapsed += frameElapsed; + + if (updateElapsed >= FPS_SAMPLE_RATE) + { + static int sampleIndex = 0; + int i; + + fps_samples[sampleIndex] = frameElapsed; + + sampleIndex++; + if (sampleIndex >= NUM_FPS_SAMPLES) + sampleIndex = 0; + + averageFPS = 0.0; + for (i = 0; i < NUM_FPS_SAMPLES; i++) + { + averageFPS += fps_samples[i]; + } + + if (averageFPS > 0.0) + { + averageFPS = 1.0 / (averageFPS / NUM_FPS_SAMPLES); + } + } + + while (updateElapsed >= FPS_SAMPLE_RATE) + { + updateElapsed -= FPS_SAMPLE_RATE; + } +#else + // Direct, unsampled counter. + averageFPS = 1.0 / frameElapsed; +#endif +} void SCR_DisplayTicRate(void) { - tic_t i; - tic_t ontic = I_GetTime(); - tic_t totaltics = 0; INT32 ticcntcolor = 0; const INT32 h = vid.height-(8*vid.dupy); + UINT32 cap = R_GetFramerateCap(); + double fps = round(averageFPS); if (gamestate == GS_NULL) return; - for (i = lasttic + 1; i < TICRATE+lasttic && i < ontic; ++i) - fpsgraph[i % TICRATE] = false; - - fpsgraph[ontic % TICRATE] = true; - - for (i = 0;i < TICRATE;++i) - if (fpsgraph[i]) - ++totaltics; - - if (totaltics <= TICRATE/2) ticcntcolor = V_REDMAP; - else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP; + if (cap > 0) + { + if (fps <= cap / 2.0) ticcntcolor = V_REDMAP; + else if (fps <= cap * 0.90) ticcntcolor = V_YELLOWMAP; + else ticcntcolor = V_GREENMAP; + } + else + { + ticcntcolor = V_GREENMAP; + } if (cv_ticrate.value == 2) // compact counter - V_DrawString(vid.width-(16*vid.dupx), h, - ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%02d", totaltics)); + { + V_DrawRightAlignedString(vid.width, h, + ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%04.2f", averageFPS)); // use averageFPS directly + } else if (cv_ticrate.value == 1) // full counter { - V_DrawString(vid.width-(72*vid.dupx), h, + const char *drawnstr; + INT32 width; + + // The highest assignable cap is < 1000, so 3 characters is fine. + if (cap > 0) + drawnstr = va("%3.0f/%3u", fps, cap); + else + drawnstr = va("%4.2f", averageFPS); + + width = V_StringWidth(drawnstr, V_NOSCALESTART); + + V_DrawString(vid.width - ((7 * 8 * vid.dupx) + V_StringWidth("FPS: ", V_NOSCALESTART)), h, V_YELLOWMAP|V_NOSCALESTART|V_USERHUDTRANS, "FPS:"); - V_DrawString(vid.width-(40*vid.dupx), h, - ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%02d/%02u", totaltics, TICRATE)); + V_DrawString(vid.width - width, h, + ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, drawnstr); } - - lasttic = ontic; } void SCR_DisplayLocalPing(void) diff --git a/src/screen.h b/src/screen.h index 37695316916ad839be0e8211a8531e8addf0180d..da27aa8f083b2abfcb45572278ff4b9bbfadc4de 100644 --- a/src/screen.h +++ b/src/screen.h @@ -182,6 +182,8 @@ extern viddef_t vid; extern INT32 setmodeneeded; // mode number to set if needed, or 0 extern UINT8 setrenderneeded; +extern double averageFPS; + void SCR_ChangeRenderer(void); extern CV_PossibleValue_t cv_renderer_t[]; @@ -192,6 +194,7 @@ extern UINT8 *scr_borderpatch; // patch used to fill the view borders extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen; // wait for page flipping to end or not extern consvar_t cv_vidwait; +extern consvar_t cv_timescale; // Initialize the screen void SCR_Startup(void); @@ -211,6 +214,8 @@ void SCR_CheckDefaultMode(void); // Set the mode number which is saved in the config void SCR_SetDefaultMode(void); +void SCR_CalculateFPS(void); + FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height); // move out to main code for consistency diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 3f16e8c6bb1cadb0ba3b5f59739f7d56dd9704b1..818d0f0c4bca17fdd15059d5a92e9db8bd57d0e4 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -181,6 +181,7 @@ static char returnWadPath[256]; #include "../doomdef.h" #include "../m_misc.h" +#include "../i_time.h" #include "../i_video.h" #include "../i_sound.h" #include "../i_system.h" @@ -196,7 +197,8 @@ static char returnWadPath[256]; #include "../m_argv.h" -#include "../m_menu.h" +#include "../r_main.h" // Frame interpolation/uncapped +#include "../r_fps.h" #ifdef MAC_ALERT #include "macosx/mac_alert.h" @@ -2153,52 +2155,78 @@ ticcmd_t *I_BaseTiccmd2(void) static Uint64 timer_frequency; -static double tic_frequency; -static Uint64 tic_epoch; - -tic_t I_GetTime(void) +precise_t I_GetPreciseTime(void) { - static double elapsed; + return SDL_GetPerformanceCounter(); +} - const Uint64 now = SDL_GetPerformanceCounter(); +UINT64 I_GetPrecisePrecision(void) +{ + return SDL_GetPerformanceFrequency(); +} - elapsed += (now - tic_epoch) / tic_frequency; - tic_epoch = now; // moving epoch +static UINT32 frame_rate; - return (tic_t)elapsed; -} +static double frame_frequency; +static UINT64 frame_epoch; +static double elapsed_frames; -precise_t I_GetPreciseTime(void) +static void I_InitFrameTime(const UINT64 now, const UINT32 cap) { - return SDL_GetPerformanceCounter(); + frame_rate = cap; + frame_epoch = now; + + //elapsed_frames = 0.0; + + if (frame_rate == 0) + { + // Shouldn't be used, but just in case...? + frame_frequency = 1.0; + return; + } + + frame_frequency = timer_frequency / (double)frame_rate; } -int I_PreciseToMicros(precise_t d) +double I_GetFrameTime(void) { - // d is going to be converted into a double. So remove the highest bits - // to avoid loss of precision in the lower bits, for the (probably rare) case - // that the higher bits are actually used. - d &= ((precise_t)1 << 53) - 1; // The mantissa of a double can handle 53 bits at most. - // The resulting double from the calculation is converted first to UINT64 to avoid overflow, - // which is undefined behaviour when converting floating point values to integers. - return (int)(UINT64)(d / (timer_frequency / 1000000.0)); + const UINT64 now = SDL_GetPerformanceCounter(); + const UINT32 cap = R_GetFramerateCap(); + + if (cap != frame_rate) + { + // Maybe do this in a OnChange function for cv_fpscap? + I_InitFrameTime(now, cap); + } + + if (frame_rate == 0) + { + // Always advance a frame. + elapsed_frames += 1.0; + } + else + { + elapsed_frames += (now - frame_epoch) / frame_frequency; + } + + frame_epoch = now; // moving epoch + return elapsed_frames; } // -//I_StartupTimer +// I_StartupTimer // void I_StartupTimer(void) { timer_frequency = SDL_GetPerformanceFrequency(); - tic_epoch = SDL_GetPerformanceCounter(); - tic_frequency = timer_frequency / (double)NEWTICRATE; + I_InitFrameTime(0, R_GetFramerateCap()); + elapsed_frames = 0.0; } -void I_Sleep(void) +void I_Sleep(UINT32 ms) { - if (cv_sleep.value != -1) - SDL_Delay(cv_sleep.value); + SDL_Delay(ms); } #ifdef NEWSIGNALHANDLER diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index ab30cf0ca95297a84afe928517cb95bbb4a97410..9cec2a552aecfcc80fc9173b5d37c13d8178c5af 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1178,12 +1178,15 @@ void I_UpdateNoBlit(void) // from PrBoom's src/SDL/i_video.c static inline boolean I_SkipFrame(void) { -#if 0 +#if 1 + // While I fixed the FPS counter bugging out with this, + // I actually really like being able to pause and + // use perfstats to measure rendering performance + // without game logic changes. + return false; +#else static boolean skip = false; - if (rendermode != render_soft) - return false; - skip = !skip; switch (gamestate) @@ -1199,17 +1202,20 @@ static inline boolean I_SkipFrame(void) return false; } #endif - return false; } // // I_FinishUpdate // +static SDL_Rect src_rect = { 0, 0, 0, 0 }; + void I_FinishUpdate(void) { if (rendermode == render_none) return; //Alam: No software or OpenGl surface + SCR_CalculateFPS(); + if (I_SkipFrame()) return; @@ -1228,27 +1234,22 @@ void I_FinishUpdate(void) if (rendermode == render_soft && screens[0]) { - SDL_Rect rect; - - rect.x = 0; - rect.y = 0; - rect.w = vid.width; - rect.h = vid.height; - if (!bufSurface) //Double-Check { Impl_VideoSetupSDLBuffer(); } + if (bufSurface) { - SDL_BlitSurface(bufSurface, NULL, vidSurface, &rect); + SDL_BlitSurface(bufSurface, &src_rect, vidSurface, &src_rect); // Fury -- there's no way around UpdateTexture, the GL backend uses it anyway SDL_LockSurface(vidSurface); - SDL_UpdateTexture(texture, &rect, vidSurface->pixels, vidSurface->pitch); + SDL_UpdateTexture(texture, &src_rect, vidSurface->pixels, vidSurface->pitch); SDL_UnlockSurface(vidSurface); } + SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderCopy(renderer, texture, &src_rect, NULL); SDL_RenderPresent(renderer); } #ifdef HWRENDER @@ -1257,6 +1258,7 @@ void I_FinishUpdate(void) OglSdlFinishUpdate(cv_vidwait.value); } #endif + exposevideo = SDL_FALSE; } @@ -1473,8 +1475,15 @@ static SDL_bool Impl_CreateContext(void) int flags = 0; // Use this to set SDL_RENDERER_* flags now if (usesdl2soft) flags |= SDL_RENDERER_SOFTWARE; +#if 0 + // This shit is BROKEN. + // - The version of SDL we're using cannot toggle VSync at runtime. We'll need a new SDL version implemented to have this work properly. + // - cv_vidwait is initialized before config is loaded, so it's forced to default value at runtime, and forced off when switching. The config loading code would need restructured. + // - With both this & frame interpolation on, I_FinishUpdate takes x10 longer. At this point, it is simpler to use a standard FPS cap. + // So you can probably guess why I'm kinda over this, I'm just disabling it. else if (cv_vidwait.value) flags |= SDL_RENDERER_PRESENTVSYNC; +#endif if (!renderer) renderer = SDL_CreateRenderer(window, -1, flags); @@ -1593,6 +1602,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(); @@ -1610,6 +1640,11 @@ INT32 VID_SetMode(INT32 modeNum) vid.modenum = modeNum; //Impl_SetWindowName("SRB2 "VERSIONSTRING); + src_rect.w = vid.width; + src_rect.h = vid.height; + + refresh_rate = VID_GetRefreshRate(); + VID_CheckRenderer(); return SDL_TRUE; } @@ -1961,3 +1996,13 @@ void I_GetCursorPosition(INT32 *x, INT32 *y) { SDL_GetMouseState(x, y); } + +UINT32 I_GetRefreshRate(void) +{ + // 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 6c9a0eeca1461dfdfa7630b0d33ab379673edfdd..ed130c912c5c97bc9ef8f38aa1eba5872f67fe3c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -43,8 +43,11 @@ #endif #include "lua_hud.h" +#include "lua_hudlib_drawlist.h" #include "lua_hook.h" +#include "r_fps.h" + UINT16 objectsdrawn = 0; // @@ -161,6 +164,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 // @@ -420,6 +426,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. @@ -1402,7 +1411,12 @@ void ST_drawTitleCard(void) lt_lasttic = lt_ticker; luahook: - LUA_HUDHOOK(titlecard); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_titlecard); + LUA_HUDHOOK(titlecard, luahuddrawlist_titlecard); + } + LUA_HUD_DrawList(luahuddrawlist_titlecard); } // @@ -2526,7 +2540,7 @@ static void ST_doHuntIconsAndSound(void) interval = newinterval; } - if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0) + if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic) S_StartSound(NULL, sfx_emfind); } @@ -2588,7 +2602,7 @@ static void ST_doItemFinderIconsAndSound(void) } - if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0) + if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic) S_StartSound(NULL, sfx_emfind); } @@ -2742,7 +2756,12 @@ static void ST_overlayDrawer(void) ST_drawPowerupHUD(); // same as it ever was... if (!(netgame || multiplayer) || !hu_showscores) - LUA_HUDHOOK(game); + { + if (renderisnewtic) + { + LUA_HUDHOOK(game, luahuddrawlist_game); + } + } // draw level title Tails if (stagetitle && (!WipeInAction) && (!WipeStageTitle)) @@ -2820,6 +2839,10 @@ void ST_Drawer(void) if (st_overlay) { + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_game); + } // No deadview! stplyr = &players[displayplayer]; ST_overlayDrawer(); @@ -2829,5 +2852,7 @@ void ST_Drawer(void) stplyr = &players[secondarydisplayplayer]; ST_overlayDrawer(); } + + LUA_HUD_DrawList(luahuddrawlist_game); } } diff --git a/src/w_wad.c b/src/w_wad.c index 7e5f056cfa0ad51e51d282001e4393f69505fa5e..42c6bf83be9e7dd8a89e67aee6aa7373d96bc53e 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -59,6 +59,7 @@ #include "r_textures.h" #include "r_patch.h" #include "r_picformats.h" +#include "i_time.h" #include "i_system.h" #include "i_video.h" // rendermode #include "md5.h" diff --git a/src/y_inter.c b/src/y_inter.c index 34e58494f1b9b9cc1c04d068d2db43b1cccf2c7d..4f7b0499df639b87de71ea8b036ef38950f5c559 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" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -83,6 +84,10 @@ typedef union INT32 passedx3; INT32 passedx4; + INT32 emeraldbounces; + INT32 emeraldmomy; + INT32 emeraldy; + y_bonus_t bonuses[2]; patch_t *bonuspatches[2]; @@ -161,6 +166,8 @@ static INT32 endtic = -1; intertype_t intertype = int_none; intertype_t intermissiontypes[NUMGAMETYPES]; +static huddrawlist_h luahuddrawlist_intermission; + static void Y_RescaleScreenBuffer(void); static void Y_AwardCoopBonuses(void); static void Y_AwardSpecialStageBonus(void); @@ -429,7 +436,13 @@ void Y_IntermissionDrawer(void) else if (bgtile) V_DrawPatchFill(bgtile); - LUA_HUDHOOK(intermission); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_intermission); + LUA_HUDHOOK(intermission, luahuddrawlist_intermission); + } + LUA_HUD_DrawList(luahuddrawlist_intermission); + if (!LUA_HudEnabled(hud_intermissiontally)) goto skiptallydrawer; @@ -646,7 +659,6 @@ void Y_IntermissionDrawer(void) } // draw the emeralds - //if (intertic & 1) if (LUA_HudEnabled(hud_intermissionemeralds)) { boolean drawthistic = !(ALL7EMERALDS(emeralds) && (intertic & 1)); @@ -663,10 +675,6 @@ void Y_IntermissionDrawer(void) } else if (em < 7) { - static UINT8 emeraldbounces = 0; - static INT32 emeraldmomy = 20; - static INT32 emeraldy = -40; - if (drawthistic) for (i = 0; i < 7; ++i) { @@ -677,45 +685,15 @@ void Y_IntermissionDrawer(void) emeraldx = 152 + (em-3)*28; - if (intertic <= 1) - { - emeraldbounces = 0; - emeraldmomy = 20; - emeraldy = -40; - } - else + if (intertic > 1) { - if (!stagefailed) - { - if (emeraldbounces < 3) - { - emeraldy += (++emeraldmomy); - if (emeraldy > 74) - { - S_StartSound(NULL, sfx_tink); // tink - emeraldbounces++; - emeraldmomy = -(emeraldmomy/2); - emeraldy = 74; - } - } - } - else + if (stagefailed && data.spec.emeraldy < (vid.height/vid.dupy)+16) { - if (emeraldy < (vid.height/vid.dupy)+16) - { - emeraldy += (++emeraldmomy); - emeraldx += intertic - 6; - } - if (emeraldbounces < 1 && emeraldy > 74) - { - S_StartSound(NULL, sfx_shldls); // nope - emeraldbounces++; - emeraldmomy = -(emeraldmomy/2); - emeraldy = 74; - } + emeraldx += intertic - 6; } + if (drawthistic) - V_DrawScaledPatch(emeraldx, emeraldy, 0, emeraldpics[0][em]); + V_DrawScaledPatch(emeraldx, data.spec.emeraldy, 0, emeraldpics[0][em]); } } } @@ -1153,6 +1131,45 @@ void Y_Ticker(void) S_ChangeMusicInternal("_clear", false); // don't loop it tallydonetic = -1; } + + // emerald bounce + if (intertic <= 1) + { + data.spec.emeraldbounces = 0; + data.spec.emeraldmomy = 20; + data.spec.emeraldy = -40; + } + else + { + if (!stagefailed) + { + if (data.spec.emeraldbounces < 3) + { + data.spec.emeraldy += (++data.spec.emeraldmomy); + if (data.spec.emeraldy > 74) + { + S_StartSound(NULL, sfx_tink); // tink + data.spec.emeraldbounces++; + data.spec.emeraldmomy = -(data.spec.emeraldmomy/2); + data.spec.emeraldy = 74; + } + } + } + else + { + if (data.spec.emeraldy < (vid.height/vid.dupy)+16) + { + data.spec.emeraldy += (++data.spec.emeraldmomy); + } + if (data.spec.emeraldbounces < 1 && data.spec.emeraldy > 74) + { + S_StartSound(NULL, sfx_shldls); // nope + data.spec.emeraldbounces++; + data.spec.emeraldmomy = -(data.spec.emeraldmomy/2); + data.spec.emeraldy = 74; + } + } + } if (intertic < 2*TICRATE) // TWO second pause before tally begins, thank you mazmazz return; @@ -1583,6 +1600,9 @@ void Y_StartIntermission(void) default: break; } + + LUA_HUD_DestroyDrawList(luahuddrawlist_intermission); + luahuddrawlist_intermission = LUA_HUD_CreateDrawList(); } //