diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9854567e..335e46a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -81,6 +81,7 @@ set(SRB2_CORE_HEADERS i_sound.h i_system.h i_tcp.h + i_time.h i_video.h info.h keys.h @@ -118,6 +119,7 @@ set(SRB2_CORE_RENDER_SOURCES r_bsp.c r_data.c r_draw.c + r_fps.c r_main.c r_plane.c r_segs.c @@ -129,6 +131,7 @@ set(SRB2_CORE_RENDER_SOURCES r_data.h r_defs.h r_draw.h + r_fps.h r_local.h r_main.h r_plane.h @@ -158,6 +161,7 @@ set(SRB2_CORE_GAME_SOURCES p_tick.c p_user.c k_kart.c + i_time.c p_local.h p_maputl.h @@ -248,6 +252,7 @@ if(${SRB2_CONFIG_HAVE_BLUA}) lua_consolelib.c lua_hooklib.c lua_hudlib.c + lua_hudlib_drawlist.c lua_infolib.c lua_maplib.c lua_mathlib.c @@ -260,6 +265,7 @@ if(${SRB2_CONFIG_HAVE_BLUA}) set(SRB2_LUA_HEADERS lua_hook.h lua_hud.h + lua_hudlib_drawlist.h lua_libs.h lua_script.h ) diff --git a/src/Makefile b/src/Makefile index 6ea7fd78..7836f1f7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -587,6 +587,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_bsp.o \ $(OBJDIR)/r_data.o \ $(OBJDIR)/r_draw.o \ + $(OBJDIR)/r_fps.o \ $(OBJDIR)/r_main.o \ $(OBJDIR)/r_plane.o \ $(OBJDIR)/r_segs.o \ @@ -602,6 +603,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/mserv.o \ $(OBJDIR)/http-mserv.o\ $(OBJDIR)/i_tcp.o \ + $(OBJDIR)/i_time.o \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ $(OBJDIR)/b_bot.o \ diff --git a/src/android/i_system.c b/src/android/i_system.c index 222f89f4..51a96d93 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 c16976b0..7c14c44a 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -270,8 +270,8 @@ void B_RespawnBot(INT32 playernum) player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots]; player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; - P_TeleportMove(tails, x, y, z); + P_SetOrigin(tails, x, y, z); P_SetPlayerMobjState(tails, S_KART_STND1); // SRB2kart - was S_PLAY_FALL1 P_SetScale(tails, sonic->scale); - tails->destscale = sonic->destscale; + tails->destscale = tails->old_scale = sonic->destscale; } diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg index 2ed5a535..f89dc39a 100644 --- a/src/blua/Makefile.cfg +++ b/src/blua/Makefile.cfg @@ -50,4 +50,5 @@ OBJS:=$(OBJS) \ $(OBJDIR)/lua_skinlib.o \ $(OBJDIR)/lua_thinkerlib.o \ $(OBJDIR)/lua_maplib.o \ - $(OBJDIR)/lua_hudlib.o + $(OBJDIR)/lua_hudlib.o \ + $(OBJDIR)/lua_hudlib_drawlist.o diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d0b6f13e..e8dee0a4 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -17,6 +17,7 @@ #include //for unlink #endif +#include "i_time.h" #include "i_net.h" #include "i_system.h" #include "i_video.h" @@ -2502,7 +2503,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; } @@ -5540,8 +5544,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!! { @@ -5593,10 +5599,17 @@ void TryRunTics(tic_t realtics) if (player_joining) { hu_stopped = true; - return; + return false; } - if (neededtic > gametic) + ticking = neededtic > gametic; + + if (player_joining) + { + return false; + } + + if (ticking) { if (advancedemo) D_StartTitle(); @@ -5620,6 +5633,8 @@ void TryRunTics(tic_t realtics) { hu_stopped = true; } + + return ticking; } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index b7fc56ec..0ce20f1a 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -595,7 +595,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. diff --git a/src/d_main.c b/src/d_main.c index 2c02565d..5d29a90f 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -50,6 +50,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #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" @@ -73,6 +74,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "dehacked.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 @@ -268,7 +270,7 @@ void D_ProcessEvents(void) // added comment : there is a wipe eatch change of the gamestate gamestate_t wipegamestate = GS_LEVEL; -static void D_Display(void) +static boolean D_Display(void) { boolean forcerefresh = false; static boolean wipe = false; @@ -278,7 +280,7 @@ static void D_Display(void) if (!dedicated) { if (nodrawers) - return; // for comparative timing/profiling + return false; // for comparative timing/profiling // check for change of screen size (video mode) if (setmodeneeded && !wipe) @@ -343,7 +345,7 @@ static void D_Display(void) } if (dedicated) //bail out after wipe logic - return; + return false; // do buffered drawing switch (gamestate) @@ -430,6 +432,8 @@ static void D_Display(void) // draw the view directly if (cv_renderview.value && !automapactive) { + R_ApplyLevelInterpolators(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); + for (i = 0; i <= splitscreen; i++) { if (players[displayplayers[i]].mo || players[displayplayers[i]].playerstate == PST_DEAD) @@ -502,6 +506,8 @@ static void D_Display(void) V_DoPostProcessor(i, postimgtype[i], postimgparam[i]); } } + + R_RestoreLevelInterpolators(); } if (lastdraw) @@ -607,8 +613,10 @@ static void D_Display(void) if (cv_shittyscreen.value) V_DrawVhsEffect(cv_shittyscreen.value == 2); - I_FinishUpdate(); // page flip or blit buffer + return true; // Do I_FinishUpdate in the main loop } + + return false; } // ========================================================================= @@ -619,7 +627,13 @@ 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; + + boolean interp = false; + boolean doDisplay = false; + boolean screenUpdate = false; if (dedicated) server = true; @@ -631,6 +645,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() @@ -657,6 +672,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; @@ -668,7 +696,11 @@ void D_SRB2Loop(void) realtics = entertic - oldentertics; oldentertics = entertic; - refreshdirmenu = 0; // not sure where to put this, here as good as any? + if (demo.playback && gamestate == GS_LEVEL) + { + // Nicer place to put this. + realtics = realtics * cv_playbackspeed.value; + } #ifdef DEBUGFILE if (!realtics) @@ -676,45 +708,66 @@ void D_SRB2Loop(void) debugload--; #endif - if (!realtics && !singletics) - { - I_Sleep(); - continue; - } + interp = R_UsingFrameInterpolation() && !dedicated; + doDisplay = screenUpdate = 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; + refreshdirmenu = 0; // not sure where to put this, here as good as any? - // process tics (but maybe not if realtic == 0) - TryRunTics(realtics); - - 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 (moviemode) - M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. - M_DoScreenShot(); + if (lastdraw || singletics || gametic > rendergametic) + { + rendergametic = gametic; + rendertimeout = entertic + TICRATE/17; + + doDisplay = true; + } + else if (rendertimeout < entertic) // in case the server hang or netsplit + { + doDisplay = true; + } + + renderisnewtic = true; } - else if (rendertimeout < entertic) // in case the server hang or netsplit + else { - D_Display(); + renderisnewtic = false; + } - if (moviemode) - M_SaveFrame(); - if (takescreenshot) // Only take screenshots after drawing. - M_DoScreenShot(); + if (interp) + { + renderdeltatics = FLOAT_TO_FIXED(deltatics); + + if (!(paused || P_AutoPause())) + { + rendertimefrac = g_time.timefrac; + } + else + { + rendertimefrac = FRACUNIT; + } + } + else + { + renderdeltatics = realtics * FRACUNIT; + rendertimefrac = FRACUNIT; + } + + if (interp || doDisplay) + { + screenUpdate = D_Display(); } // consoleplayer -> displayplayers (hear sounds from viewpoint) @@ -737,6 +790,34 @@ void D_SRB2Loop(void) Discord_RunCallbacks(); } #endif + + // I_FinishUpdate is now here instead of D_Display, + // because it synchronizes it more closely with the frame counter. + if (screenUpdate == true) + { + I_FinishUpdate(); // page flip or blit buffer + } + + // 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; + + // Only take screenshots after drawing. + if (moviemode) + M_SaveFrame(); + if (takescreenshot) + M_DoScreenShot(); } } @@ -1189,8 +1270,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(); @@ -1520,6 +1601,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) + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); if (M_CheckParm("-gametype") && M_IsNextParm()) diff --git a/src/d_net.c b/src/d_net.c index 3b1bad87..d974d6cf 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 e3f861ae..359efac5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -16,6 +16,7 @@ #include "console.h" #include "command.h" +#include "i_time.h" #include "i_system.h" #include "g_game.h" #include "hu_stuff.h" diff --git a/src/d_netfil.c b/src/d_netfil.c index d32d4d14..01a43fa4 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -50,6 +50,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 114674ff..2ee371de 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -388,6 +388,7 @@ typedef struct player_s // SRB2kart stuff INT32 kartstuff[NUMKARTSTUFF]; angle_t frameangle; // for the player add the ability to have the sprite only face other angles + angle_t old_frameangle, old_frameangle2; INT16 lturn_max[MAXPREDICTTICS]; // What's the expected turn value for full-left for a number of frames back (to account for netgame latency)? INT16 rturn_max[MAXPREDICTTICS]; // Ditto but for full-right diff --git a/src/doomtype.h b/src/doomtype.h index 67491abb..ad96b979 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -405,4 +405,6 @@ typedef UINT32 tic_t; #define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) #endif +typedef UINT64 precise_t; + #endif //__DOOMTYPE__ diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c index a3fe3077..1696672b 100644 --- a/src/dummy/i_system.c +++ b/src/dummy/i_system.c @@ -11,12 +11,15 @@ UINT32 I_GetFreeMem(UINT32 *total) return 0; } -tic_t I_GetTime(void) -{ +void I_Sleep(UINT32 ms){} + +precise_t I_GetPreciseTime(void) { return 0; } -void I_Sleep(void){} +UINT64 I_GetPrecisePrecision(void) { + return 1000000; +} void I_GetEvent(void){} diff --git a/src/f_finale.c b/src/f_finale.c index 4c39ec9b..e046f3a3 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -19,6 +19,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" @@ -77,7 +78,6 @@ static INT32 cutscene_textcount = 0; static INT32 cutscene_textspeed = 0; static UINT8 cutscene_boostspeed = 0; static tic_t cutscene_lasttextwrite = 0; - // // This alters the text string cutscene_disptext. // Use the typical string drawing functions to display it. @@ -249,9 +249,9 @@ void F_StartIntro(void) } // -// F_IntroDrawScene +// F_IntroDrawer // -static void F_IntroDrawScene(void) +void F_IntroDrawer(void) { boolean highres = false; INT32 cx = 8, cy = 128; @@ -293,64 +293,6 @@ static void F_IntroDrawScene(void) V_DrawString(cx, cy, 0, cutscene_disptext); } -// -// F_IntroDrawer -// -void F_IntroDrawer(void) -{ - if (timetonext <= 0) - { - if (intro_scenenum == 0) - { - if (rendermode != render_none) - { - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(99,true); - } - - // Stay on black for a bit. =) - { - tic_t quittime; - quittime = I_GetTime() + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds - while (quittime > I_GetTime()) - { - I_OsPolling(); - I_UpdateNoBlit(); -#ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); -#endif - M_Drawer(); // menu is drawn even on top of wipes -#ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); -#endif - I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 - } - } - - D_StartTitle(); - // Yes, this is a weird hack, we need to force a wipe for this because the game state has changed in the middle of where it would normally wipe - // Need to set the wipe start and then draw the first frame of the title screen to get it working - F_WipeStartScreen(); - F_TitleScreenDrawer(); - wipegamestate = -1; // force a wipe - return; - } - - F_NewCutscene(introtext[++intro_scenenum]); - timetonext = introscenetime[intro_scenenum]; - - F_WipeStartScreen(); - wipegamestate = -1; - animtimer = stoptimer = 0; - } - - intro_curtime = introscenetime[intro_scenenum] - timetonext; - - F_IntroDrawScene(); -} - // // F_IntroTicker // @@ -1318,6 +1260,22 @@ 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) @@ -1325,6 +1283,7 @@ static void F_AdvanceToNextScene(void) F_EndCutScene(); return; } + ++scenenum; timetonext = 0; @@ -1340,7 +1299,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; @@ -1350,6 +1308,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); + } } void F_EndCutScene(void) @@ -1468,8 +1434,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 8bcb1278..9909de0f 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -21,6 +21,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" @@ -365,7 +366,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; #ifdef HWRENDER diff --git a/src/g_game.c b/src/g_game.c index 1fb2ceae..4775488b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -20,6 +20,7 @@ #include "filesrch.h" // for refreshdirmenu #include "p_setup.h" #include "p_saveg.h" +#include "i_time.h" #include "i_system.h" #include "am_map.h" #include "m_random.h" @@ -50,6 +51,7 @@ #include "m_cond.h" // condition sets #include "md5.h" // demo checksums #include "k_kart.h" // SRB2kart +#include "r_fps.h" // frame interpolation/uncapped #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -2413,6 +2415,10 @@ void G_Ticker(boolean run) ST_Ticker(); AM_Ticker(); HU_Ticker(); + if (run) + { + R_UpdateViewInterpolation(); + } } break; @@ -6131,7 +6137,7 @@ void G_ReadMetalTic(mobj_t *metal) // Read changes from the tic if (ziptic & GZT_XYZ) { - P_TeleportMove(metal, READFIXED(metal_p), READFIXED(metal_p), READFIXED(metal_p)); + P_MoveOrigin(metal, READFIXED(metal_p), READFIXED(metal_p), READFIXED(metal_p)); oldmetal.x = metal->x; oldmetal.y = metal->y; oldmetal.z = metal->z; diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index bf8eb40f..893222ef 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -46,7 +46,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, FSurfaceInfo *Surface); +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, FSurfaceInfo *Surface); EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *stransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 9b7b31e4..0de8ca3d 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -27,6 +27,7 @@ #include "../v_video.h" #include "../p_local.h" #include "../p_setup.h" +#include "../r_fps.h" #include "../r_local.h" #include "../r_bsp.h" // R_NoEncore #include "../r_main.h" // cv_fov @@ -2389,6 +2390,7 @@ 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) @@ -4139,14 +4141,26 @@ void HWR_ProjectSprite(mobj_t *thing) const boolean papersprite = (thing->frame & FF_PAPERSPRITE); INT32 heightsec, phs; + // uncapped/interpolation + interpmobjstate_t interp = {0}; + if (!thing) return; + + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } else - this_scale = FIXED_TO_FLOAT(thing->scale); + { + R_InterpolateMobjState(thing, FRACUNIT, &interp); + } + + this_scale = FIXED_TO_FLOAT(interp.scale); // transform the origin point - tr_x = FIXED_TO_FLOAT(thing->x) - gr_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - gr_viewy; + tr_x = FIXED_TO_FLOAT(interp.x) - gr_viewx; + tr_y = FIXED_TO_FLOAT(interp.y) - gr_viewy; // rotation around vertical axis tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin); @@ -4156,8 +4170,8 @@ void HWR_ProjectSprite(mobj_t *thing) return; // 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 @@ -4192,10 +4206,7 @@ void HWR_ProjectSprite(mobj_t *thing) I_Error("sprframes NULL for sprite %d\n", thing->sprite); #endif - if (thing->player) - ang = R_PointToAngle (thing->x, thing->y) - thing->player->frameangle; - else - ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if (sprframe->rotate == SRF_SINGLE) { @@ -4240,8 +4251,8 @@ void HWR_ProjectSprite(mobj_t *thing) if (papersprite) { - rightsin = FIXED_TO_FLOAT(FINESINE((thing->angle)>>ANGLETOFINESHIFT)); - rightcos = FIXED_TO_FLOAT(FINECOSINE((thing->angle)>>ANGLETOFINESHIFT)); + rightsin = FIXED_TO_FLOAT(FINESINE(interp.angle >> ANGLETOFINESHIFT)); + rightcos = FIXED_TO_FLOAT(FINECOSINE(interp.angle >> ANGLETOFINESHIFT)); } else { @@ -4268,12 +4279,12 @@ void HWR_ProjectSprite(mobj_t *thing) if (thing->eflags & MFE_VERTICALFLIP) { - gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; + gz = FIXED_TO_FLOAT(interp.z + thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale; } else { - gzt = FIXED_TO_FLOAT(thing->z) + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; + gzt = FIXED_TO_FLOAT(interp.z) + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; gz = gzt - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale; } @@ -4292,12 +4303,12 @@ void HWR_ProjectSprite(mobj_t *thing) if (heightsec != -1 && phs != -1) // only clip things which are in special sectors { if (gr_viewz < FIXED_TO_FLOAT(sectors[phs].floorheight) ? - FIXED_TO_FLOAT(thing->z) >= FIXED_TO_FLOAT(sectors[heightsec].floorheight) : + FIXED_TO_FLOAT(interp.z) >= FIXED_TO_FLOAT(sectors[heightsec].floorheight) : gzt < FIXED_TO_FLOAT(sectors[heightsec].floorheight)) return; if (gr_viewz > FIXED_TO_FLOAT(sectors[phs].ceilingheight) ? gzt < FIXED_TO_FLOAT(sectors[heightsec].ceilingheight) && gr_viewz >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight) : - FIXED_TO_FLOAT(thing->z) >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight)) + FIXED_TO_FLOAT(interp.z) >= FIXED_TO_FLOAT(sectors[heightsec].ceilingheight)) return; } @@ -4375,9 +4386,25 @@ void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) unsigned rot = 0; UINT8 flip; + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + if (!thing) + return; + + // 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) - gr_viewx; - tr_y = FIXED_TO_FLOAT(thing->y) - gr_viewy; + tr_x = FIXED_TO_FLOAT(interp.x) - gr_viewx; + tr_y = FIXED_TO_FLOAT(interp.y) - gr_viewy; // rotation around vertical axis tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin); @@ -4386,8 +4413,8 @@ 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) @@ -4912,7 +4939,8 @@ void HWR_DoPostProcessor(player_t *player) { // 10 by 10 grid. 2 coordinates (xy) float v[SCREENVERTS][SCREENVERTS][2]; - double disStart = leveltime; + static double disStart = 0; + UINT8 x, y; INT32 WAVELENGTH; INT32 AMPLITUDE; @@ -4921,15 +4949,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++) @@ -4942,6 +4970,8 @@ void HWR_DoPostProcessor(player_t *player) } } HWD.pfnPostImgRedraw(v); + if (!(paused || P_AutoPause())) + 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 7d97bd06..5c1927d1 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" @@ -1128,6 +1129,16 @@ void HWR_DrawMD2(gr_vissprite_t *spr) spritedef_t *sprdef; spriteframe_t *sprframe; 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) @@ -1233,14 +1244,17 @@ void HWR_DrawMD2(gr_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; } //FIXME: this is not yet correct frame = (spr->mobj->frame & FF_FRAMEMASK) % md2->model->meshes[0].numFrames; #ifdef USE_MODEL_NEXTFRAME + // Interpolate the model interpolation. (lol) + tics -= FixedToFloat(rendertimefrac); + if (cv_grmdls.value == 1 && tics <= durs) { // frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation @@ -1265,13 +1279,13 @@ void HWR_DrawMD2(gr_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 (spr->mobj->eflags & MFE_VERTICALFLIP) - 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)->spritedef; @@ -1282,16 +1296,13 @@ void HWR_DrawMD2(gr_vissprite_t *spr) if (sprframe->rotate) { - fixed_t anglef; - if (spr->mobj->player) - anglef = AngleFixed(spr->mobj->player->frameangle); - else - anglef = AngleFixed(spr->mobj->angle); + 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); } p.anglex = 0.0f; @@ -1311,7 +1322,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr) #endif // SRB2CBTODO: MD2 scaling support - finalscale *= FIXED_TO_FLOAT(spr->mobj->scale); + finalscale *= FIXED_TO_FLOAT(interp.scale); p.flip = atransform.flip; #ifdef USE_FTRANSFORM_MIRROR diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index eef723e5..91ba8e50 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2865,7 +2865,7 @@ EXPORT void HWRAPI(CreateModelVBOs) (model_t *model) #define BUFFER_OFFSET(i) ((char*)(i)) -static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, FSurfaceInfo *Surface) +static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, FSurfaceInfo *Surface) { static GLRGBAFloat poly = {0,0,0,0}; static GLRGBAFloat tint = {0,0,0,0}; @@ -2884,11 +2884,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; @@ -3063,7 +3063,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, FSurfaceInfo *Surface) +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, FSurfaceInfo *Surface) { DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, Surface); } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 1d0f8b4c..bba644d9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -48,6 +48,7 @@ #ifdef HAVE_BLUA #include "lua_hud.h" +#include "lua_hudlib_drawlist.h" #include "lua_hook.h" #endif @@ -90,6 +91,10 @@ static boolean headsupactive = false; boolean hu_showscores; // draw rankings static char hu_tick; +#ifdef HAVE_BLUA +static huddrawlist_h luahuddrawlist_scores; +#endif + patch_t *rflagico; patch_t *bflagico; patch_t *rmatcico; @@ -176,6 +181,8 @@ static INT32 cechoflags = 0; // HEADS UP INIT //====================================================================== +static tic_t resynch_ticker = 0; + #ifndef NONET // just after static void Command_Say_f(void); @@ -385,12 +392,12 @@ static INT16 addy = 0; // use this to make the messages scroll smoothly when one static void HU_removeChatText_Mini(void) { - // MPC: Don't create new arrays, just iterate through an existing one + // MPC: Don't create new arrays, just iterate through an existing one size_t i; - for(i=0;i 0) + chat_scrolltime--; + } + + if (cechotimer > 0) --cechotimer; } #ifndef NONET @@ -2204,8 +2220,6 @@ static void HU_DrawCEcho(void) echoptr = line; echoptr++; } - - --cechotimer; } // @@ -2305,9 +2319,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 @@ -2317,30 +2328,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 0) - stop_spamming[i]--; - } - - // handle chat timers - for (i=0; (i 0) - chat_timers[i]--; - else - HU_removeChatText_Mini(); - } - } #endif if (cechotimer) @@ -2363,7 +2354,12 @@ void HU_Drawer(void) #endif HU_DrawRankings(); #ifdef HAVE_BLUA - LUAh_ScoresHUD(); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_scores); + LUAh_ScoresHUD(luahuddrawlist_scores); + } + LUA_HUD_DrawList(luahuddrawlist_scores); #endif } if (demo.playback) @@ -2398,12 +2394,9 @@ void HU_Drawer(void) // draw desynch text if (hu_resynching) { - 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 3e589c69..ba05d06e 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -42,15 +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. 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 The I_Sleep function +/** \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. + */ +UINT64 I_GetPrecisePrecision(void); + +/** \brief Get the current time in rendering tics, including fractions. +*/ +double I_GetFrameTime(void); + +/** \brief Sleeps for the given duration in milliseconds. Depending on the + operating system's scheduler, the calling thread may give up its + time slice for a longer duration. The implementation should give a + best effort to sleep for the given duration, without spin-locking. + Calling code should check the current precise time after sleeping + and not assume the thread has slept for the expected duration. \return void */ -void I_Sleep(void); +void I_Sleep(UINT32 ms); /** \brief Get events diff --git a/src/i_time.c b/src/i_time.c new file mode 100644 index 00000000..1e5030b7 --- /dev/null +++ b/src/i_time.c @@ -0,0 +1,117 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file i_time.c +/// \brief Timing for the system layer. + +#include "i_time.h" + +#include + +#include "command.h" +#include "doomtype.h" +#include "d_netcmd.h" +#include "m_fixed.h" +#include "i_system.h" + +timestate_t g_time; + +static CV_PossibleValue_t timescale_cons_t[] = {{FRACUNIT/20, "MIN"}, {20*FRACUNIT, "MAX"}, {0, NULL}}; +consvar_t cv_timescale = {"timescale", "1.0", CV_NETVAR|CV_CHEAT|CV_FLOAT, timescale_cons_t, NULL, FRACUNIT, NULL, NULL, 0, 0, NULL}; + +static precise_t enterprecise, oldenterprecise; +static fixed_t entertic, oldentertics; +static double tictimer; + +tic_t I_GetTime(void) +{ + return g_time.time; +} + +void I_InitializeTime(void) +{ + g_time.time = 0; + g_time.timefrac = 0; + + enterprecise = 0; + oldenterprecise = 0; + tictimer = 0.0; + + CV_RegisterVar(&cv_timescale); + + // I_StartupTimer is preserved for potential subsystems that need to setup + // timing information for I_GetPreciseTime and sleeping + I_StartupTimer(); +} + +void I_UpdateTime(fixed_t timescale) +{ + double ticratescaled; + double elapsedseconds; + tic_t realtics; + + // get real tics + ticratescaled = (double)TICRATE * FIXED_TO_FLOAT(timescale); + + enterprecise = I_GetPreciseTime(); + elapsedseconds = (double)(enterprecise - oldenterprecise) / I_GetPrecisePrecision(); + tictimer += elapsedseconds; + while (tictimer > 1.0/ticratescaled) + { + entertic += 1; + tictimer -= 1.0/ticratescaled; + } + realtics = entertic - oldentertics; + oldentertics = entertic; + oldenterprecise = enterprecise; + + // Update global time state + g_time.time += realtics; + { + double fractional, integral; + fractional = modf(tictimer * ticratescaled, &integral); + g_time.timefrac = FLOAT_TO_FIXED(fractional); + } +} + +void I_SleepDuration(precise_t duration) +{ + UINT64 precision = I_GetPrecisePrecision(); + INT32 sleepvalue = cv_sleep.value; + UINT64 delaygranularity; + precise_t cur; + precise_t dest; + + { + double gran = round(((double)(precision / 1000) * sleepvalue * 2.1)); + delaygranularity = (UINT64)gran; + } + + cur = I_GetPreciseTime(); + dest = cur + duration; + + // the reason this is not dest > cur is because the precise counter may wrap + // two's complement arithmetic is our friend here, though! + // e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1 + // 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3 + while ((INT64)(dest - cur) > 0) + { + // If our cv_sleep value exceeds the remaining sleep duration, use the + // hard sleep function. + if (sleepvalue > 0 && (dest - cur) > delaygranularity) + { + I_Sleep(sleepvalue); + } + + // Otherwise, this is a spinloop. + + cur = I_GetPreciseTime(); + } +} diff --git a/src/i_time.h b/src/i_time.h new file mode 100644 index 00000000..cab36133 --- /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 4bb2c582..60252287 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -135,4 +135,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 5e389b48..3d00b896 100644 --- a/src/info.c +++ b/src/info.c @@ -182,10 +182,10 @@ state_t states[NUMSTATES] = // 1-Up Box Sprites (uses player sprite) // Kart: hide for now, fix for R2 - {SPR_NULL, 0, 2, {NULL}, 0, 16, S_PLAY_BOX2}, // S_PLAY_BOX1 - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_PLAY_BOX1}, // S_PLAY_BOX2 + {SPR_NULL, 0, 2, {NULL}, 0, 18, S_PLAY_BOX2}, // S_PLAY_BOX1 + {SPR_NULL, 0, 1, {NULL}, 0, 18, S_PLAY_BOX1}, // S_PLAY_BOX2 {SPR_NULL, 0, 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_NULL, 0, 18, {NULL}, 0, 4, S_NULL}, // S_PLAY_ICON3 // Level end sign (uses player sprite) diff --git a/src/k_kart.c b/src/k_kart.c index 538d3f33..9d77b0af 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2731,7 +2731,7 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I { // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn // This should set it for FOFs - P_TeleportMove(th, th->x, th->y, th->z); + P_SetOrigin(th, th->x, th->y, th->z); // spawn on the ground if the player is on the ground if (P_MobjFlip(source) < 0) { @@ -3314,7 +3314,7 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map { // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn // This should set it for FOFs - P_TeleportMove(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you. + P_SetOrigin(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you. if (P_MobjWasRemoved(mo)) return NULL; @@ -3827,7 +3827,7 @@ void K_DropHnextList(player_t *player) { // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn // This should set it for FOFs - //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing + //P_SetOrigin(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing if (flip == 1) { @@ -4101,7 +4101,7 @@ static void K_MoveHeldObjects(player_t *player) z = player->mo->z + player->mo->height - cur->height; cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player - P_TeleportMove(cur, player->mo->x, player->mo->y, z); + P_MoveOrigin(cur, player->mo->x, player->mo->y, z); cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); cur->flags &= ~MF_NOCLIPTHING; @@ -4203,7 +4203,7 @@ static void K_MoveHeldObjects(player_t *player) P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false); if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT) - P_TeleportMove(cur, targx, targy, cur->z); + P_MoveOrigin(cur, targx, targy, cur->z); cur = cur->hnext; } @@ -4287,12 +4287,12 @@ static void K_MoveHeldObjects(player_t *player) diffy = targy - cur->y; diffz = targz - cur->z; - P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale), + P_MoveOrigin(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz); P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4)); } - P_TeleportMove(cur, targx, targy, targz); + P_MoveOrigin(cur, targx, targy, targz); K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. num = (num+1) % 2; cur = cur->hnext; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c50f53df..d3f4b3ff 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1026,7 +1026,40 @@ static int lib_pTeleportMove(lua_State *L) NOHUD 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 + 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 + 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; @@ -3038,6 +3071,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_hud.h b/src/lua_hud.h index 960195cd..07ac577d 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, @@ -36,5 +41,7 @@ extern boolean hud_running; boolean LUA_HudEnabled(enum hud option); -void LUAh_GameHUD(player_t *stplyr); -void LUAh_ScoresHUD(void); +void LUAh_GameHUD(player_t *stplyr, huddrawlist_h list); +void LUAh_ScoresHUD(huddrawlist_h list); + +#endif // __LUA_HUD_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 22e98315..63bf8c00 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -344,7 +344,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); @@ -356,7 +357,14 @@ static int libd_draw(lua_State *L) flags &= ~V_PARAMMASK; // Don't let crashes happen. - V_DrawFixedPatch(x<height/2)< maxstrength) return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength); + lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + list = (huddrawlist_h) lua_touserdata(L, -1); + lua_pop(L, 1); + if (strength == maxstrength) // Allow as a shortcut for drawfill... { - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddDrawFill(list, 0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); + else + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); + return 0; } - V_DrawFadeScreen(color, strength); + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddFadeScreen(list, color, strength); + else + V_DrawFadeScreen(color, strength); + return 0; } static int libd_drawString(lua_State *L) { + huddrawlist_h list; fixed_t x = luaL_checkinteger(L, 1); fixed_t y = luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); @@ -652,6 +719,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 @@ -691,11 +767,20 @@ static int libd_drawKartString(lua_State *L) fixed_t y = luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE); + huddrawlist_h list; flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY - V_DrawKartString(x, y, flags, str); + + lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); + list = (huddrawlist_h) lua_touserdata(L, -1); + lua_pop(L, 1); + + if (LUA_HUD_IsDrawListValid(list)) + LUA_HUD_AddDrawKartString(list, x, y, str, flags); + else + V_DrawKartString(x, y, flags, str); return 0; } @@ -800,6 +885,14 @@ static int libd_getlocaltransflag(lua_State *L) return 1; } +// Return the time elapsed for the previous frame, in tics. +static int libd_getDeltaTime(lua_State *L) +{ + HUDONLY + lua_pushfixed(L, renderdeltatics); + return 1; +} + static luaL_Reg lib_draw[] = { {"patchExists", libd_patchExists}, {"cachePatch", libd_cachePatch}, @@ -821,6 +914,7 @@ static luaL_Reg lib_draw[] = { {"renderer", libd_renderer}, {"localTransFlag", libd_getlocaltransflag}, {"drawOnMinimap", libd_drawOnMinimap}, + {"getDeltaTime", libd_getDeltaTime}, {NULL, NULL} }; @@ -956,10 +1050,13 @@ boolean LUA_HudEnabled(enum hud option) } // Hook for HUD rendering -void LUAh_GameHUD(player_t *stplayr) +void LUAh_GameHUD(player_t *stplayr, huddrawlist_h list) { if (!gL || !(hudAvailable & (1< + +#include "v_video.h" +#include "z_zone.h" + +enum drawitem_e { + DI_Draw = 0, + DI_DrawScaled, + DI_DrawNum, + DI_DrawPaddedNum, + DI_DrawFill, + DI_DrawString, + DI_DrawKartString, + 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_small, + align_smallright, + align_thin, + 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_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_AddDrawKartString( + huddrawlist_h list, + fixed_t x, + fixed_t y, + const char *str, + INT32 flags +) +{ + size_t i = AllocateDrawItem(list); + drawitem_t *item = &list->items[i]; + item->type = DI_DrawKartString; + item->x = x; + item->y = y; + item->str = CopyString(list, str); + item->flags = flags; +} + +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<y<flags, item->patch, item->colormap); + break; + case DI_DrawScaled: + V_DrawFixedPatch(item->x, item->y, item->scale, item->flags, item->patch, item->colormap); + break; + case DI_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; + // hu_font, 0.5x scale + case align_small: + V_DrawSmallString(item->x, item->y, item->flags, item->str); + break; + case align_smallright: + V_DrawRightAlignedSmallString(item->x, item->y, item->flags, item->str); + break; + // tny_font + case align_thin: + V_DrawThinString(item->x, item->y, item->flags, item->str); + break; + case align_thinright: + V_DrawRightAlignedThinString(item->x, item->y, item->flags, item->str); + break; + } + break; + case DI_DrawKartString: + V_DrawKartString(item->x, item->y, item->flags, 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 00000000..43190c41 --- /dev/null +++ b/src/lua_hudlib_drawlist.h @@ -0,0 +1,112 @@ +// 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_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_AddDrawKartString( + huddrawlist_h list, + fixed_t x, + fixed_t y, + const char *str, + INT32 flags +); +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 1361f823..2cfb0e1a 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -373,7 +373,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)); @@ -639,6 +639,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_cheat.c b/src/m_cheat.c index a884ff7e..a4aae676 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -495,7 +495,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); @@ -562,7 +562,7 @@ void Command_Teleport_f(void) CONS_Printf(M_GetText("Teleporting to %d, %d, %d...\n"), intx, inty, FixedInt(intz)); P_MapStart(); - if (!P_TeleportMove(p->mo, intx*FRACUNIT, inty*FRACUNIT, intz)) + if (!P_SetOrigin(p->mo, intx*FRACUNIT, inty*FRACUNIT, intz)) CONS_Alert(CONS_WARNING, M_GetText("Unable to teleport to that spot!\n")); else S_StartSound(p->mo, sfx_mixup); @@ -1107,13 +1107,13 @@ void OP_ObjectplaceMovement(player_t *player) if (cmd->forwardmove != 0) { P_Thrust(player->mo, player->mo->angle, (cmd->forwardmove*FRACUNIT/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) -- was disabled in practice anyways, since sidemove was suppressed { P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*FRACUNIT/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 15a61987..16f879a1 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" @@ -1255,9 +1257,10 @@ static menuitem_t OP_VideoOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 90}, {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 100}, + {IT_STRING | IT_CVAR, NULL, "FPS Cap", &cv_fpscap, 110}, #ifdef HWRENDER - {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 120}, + {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 130}, #endif }; @@ -6912,6 +6915,7 @@ static void M_DrawLoad(void) INT32 ymod = 0, offset = 0; M_DrawMenuTitle(); + fixed_t scrollfrac = FixedDiv(2, 3); if (menumovedir != 0) //movement illusion { @@ -9337,7 +9341,7 @@ static void M_HandleConnectIP(INT32 choice) // ======================== // Tails 03-02-2002 -static INT32 multi_tics; +static fixed_t multi_tics; static state_t *multi_state; // this is set before entering the MultiPlayer setup menu, @@ -9524,14 +9528,15 @@ static void M_DrawSetupMultiPlayerMenu(void) #undef iconwidth // anim the player in the box - if (--multi_tics <= 0) + multi_tics -= renderdeltatics; + while (multi_tics <= 0) { st = multi_state->nextstate; if (st != S_NULL) multi_state = &states[st]; multi_tics = multi_state->tics; - if (multi_tics == -1) - multi_tics = 15; + if (multi_tics <= -1*FRACUNIT) + multi_tics += 15*FRACUNIT; } // skin 0 is default player sprite @@ -9692,7 +9697,7 @@ static void M_SetupMultiPlayer(INT32 choice) (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; - multi_tics = multi_state->tics; + multi_tics = multi_state->tics*FRACUNIT; strcpy(setupm_name, cv_playername.string); // set for player 1 @@ -9723,7 +9728,7 @@ static void M_SetupMultiPlayer2(INT32 choice) (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; - multi_tics = multi_state->tics; + multi_tics = multi_state->tics*FRACUNIT; strcpy (setupm_name, cv_playername2.string); // set for splitscreen secondary player @@ -11226,7 +11231,8 @@ void M_QuitResponse(INT32 ch) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); V_DrawSmallScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001 I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 - I_Sleep(); + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); } } I_Quit(); diff --git a/src/m_misc.c b/src/m_misc.c index 0246a2cf..18193046 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/p_ceilng.c b/src/p_ceilng.c index 757edeba..908e493d 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" @@ -600,6 +601,9 @@ INT32 EV_DoCeiling(line_t *line, ceiling_e type) ceiling->tag = sec->tag; ceiling->type = type; firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } @@ -676,6 +680,10 @@ INT32 EV_DoCrush(line_t *line, ceiling_e type) ceiling->tag = sec->tag; ceiling->type = type; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } diff --git a/src/p_enemy.c b/src/p_enemy.c index d41c4ffb..319e59e7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8660,10 +8660,10 @@ void A_LightningFollowPlayer(mobj_t *actor) { sx = actor->target->x + FixedMul((actor->target->scale*actor->extravalue1), FINECOSINE((actor->angle)>>ANGLETOFINESHIFT)); sy = actor->target->y + FixedMul((actor->target->scale*actor->extravalue1), FINESINE((actor->angle)>>ANGLETOFINESHIFT)); - P_TeleportMove(actor, sx, sy, actor->target->z); + P_MoveOrigin(actor, sx, sy, actor->target->z); } else // else just teleport to player directly - P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z); + P_MoveOrigin(actor, actor->target->x, actor->target->y, actor->target->z); K_MatchGenericExtraFlags(actor, actor->target); // copy our target for graviflip actor->momx = actor->target->momx; @@ -10731,7 +10731,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); @@ -10776,7 +10776,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); diff --git a/src/p_floor.c b/src/p_floor.c index ccbfd6ea..27460eda 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -14,6 +14,7 @@ #include "doomdef.h" #include "doomstat.h" #include "p_local.h" +#include "r_fps.h" #include "r_state.h" #include "s_sound.h" #include "z_zone.h" @@ -705,6 +706,7 @@ void T_ContinuousFalling(levelspecthink_t *faller) { faller->sector->ceilingheight = faller->ceilingwasheight; faller->sector->floorheight = faller->floorwasheight; + R_ClearLevelInterpolatorState(&faller->thinker); } } else // Up @@ -713,6 +715,7 @@ void T_ContinuousFalling(levelspecthink_t *faller) { faller->sector->ceilingheight = faller->ceilingwasheight; faller->sector->floorheight = faller->floorwasheight; + R_ClearLevelInterpolatorState(&faller->thinker); } } @@ -2862,6 +2865,9 @@ INT32 EV_DoFloor(line_t *line, floor_e floortype) } firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&dofloor->thinker, sec, false); } return rtn; @@ -2995,6 +3001,10 @@ INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed) default: break; } + + // interpolation + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, true); } return rtn; } @@ -3089,6 +3099,10 @@ INT32 EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline) bouncer->distance = FRACUNIT; bouncer->low = 1; + // interpolation + R_CreateInterpolator_SectorPlane(&bouncer->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&bouncer->thinker, sec, true); + return 1; #undef speed #undef distance @@ -3135,6 +3149,10 @@ INT32 EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, bool faller->direction = -1; } + // interpolation + R_CreateInterpolator_SectorPlane(&faller->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&faller->thinker, sec, true); + return 1; #undef speed #undef direction @@ -3210,6 +3228,10 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating, P_SpawnMobj(foundsec->soundorg.x, foundsec->soundorg.y, elevator->direction == 1 ? elevator->sector->floorheight : elevator->sector->ceilingheight, MT_CRUMBLEOBJ); } + // interpolation + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, true); + return 1; } @@ -3250,6 +3272,10 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob block->vars[5] = FRACUNIT; // distance block->vars[6] = 1; // low + // interpolation + R_CreateInterpolator_SectorPlane(&block->thinker, roversector, false); + R_CreateInterpolator_SectorPlane(&block->thinker, roversector, true); + if (itsamonitor) { oldx = thing->x; @@ -3258,9 +3284,9 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob } P_UnsetThingPosition(thing); - thing->x = roversector->soundorg.x; - thing->y = roversector->soundorg.y; - thing->z = topheight; + thing->x = thing->old_x = roversector->soundorg.x; + thing->y = thing->old_y = roversector->soundorg.y; + thing->z = thing->old_z = topheight; thing->momz = FixedMul(6*FRACUNIT, thing->scale); P_SetThingPosition(thing); if (thing->flags & MF_SHOOTABLE) @@ -3289,9 +3315,9 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob if (itsamonitor) { P_UnsetThingPosition(tmthing); - tmthing->x = oldx; - tmthing->y = oldy; - tmthing->z = oldz; + tmthing->x = thing->old_x = oldx; + tmthing->y = thing->old_y = oldy; + tmthing->z = thing->old_z = oldz; tmthing->momx = 1; tmthing->momy = 1; P_SetThingPosition(tmthing); diff --git a/src/p_inter.c b/src/p_inter.c index 6e41d153..f48924e4 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -348,7 +348,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!special->target) return; // foolproof crash prevention check!!!!! - P_TeleportMove(player->mo, special->target->x, special->target->y, special->target->z + (48<mo, special->target->x, special->target->y, special->target->z + (48<mo->angle = special->target->angle; P_SetObjectMomZ(player->mo, 12<mo, player->mo->angle, 20<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 // ========================================================================= @@ -599,9 +624,9 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // underneath if (tmthing->eflags & MFE_VERTICALFLIP) - thing->z = tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale); + thing->z = thing->old_z = tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale); else - thing->z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale); + thing->z = thing->old_z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale); if (thing->flags & MF_SHOOTABLE) P_DamageMobj(thing, tmthing, tmthing, 1); return true; @@ -1904,7 +1929,6 @@ if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, ld) == P_PointOnLineSide(tmx + cosradius, tmy + sinradius, ld)) return true; // the line doesn't cross between collider's start or end - } // A line has been hit diff --git a/src/p_mobj.c b/src/p_mobj.c index 5c7b8bd0..0f1a52b8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -18,6 +18,7 @@ #include "hu_stuff.h" #include "p_local.h" #include "p_setup.h" +#include "r_fps.h" #include "r_main.h" #include "r_things.h" #include "r_sky.h" @@ -3795,15 +3796,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]) @@ -3823,6 +3831,7 @@ void P_RainThinker(precipmobj_t *mobj) return; mobj->z = mobj->ceilingz; + R_ResetPrecipitationMobjInterpolationState(mobj); P_SetPrecipMobjState(mobj, S_RAIN1); return; @@ -4405,7 +4414,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<x + P_ReturnThrustX(mobj, angle, dist), mobj->y + P_ReturnThrustY(mobj, angle, dist), bz + FixedMul(fz, FixedDiv(s<health > 3) { mobj->tracer->destscale = FRACUNIT + (4*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2); P_SetScale(mobj->tracer, mobj->tracer->destscale); - P_TeleportMove(mobj->tracer, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->tracer->height/2); + P_SetOrigin(mobj->tracer, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->tracer->height/2); mobj->tracer->momx = mobj->momx; mobj->tracer->momy = mobj->momy; mobj->tracer->momz = mobj->momz; @@ -5821,9 +5830,9 @@ void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on y // place us on top of them then. source->momx = source->momy = source->momz = 0; P_UnsetThingPosition(source); - source->x = tx; - source->y = ty; - source->z = tz; + source->x = source->old_x = tx; + source->y = source->old_x = ty; + source->z = source->old_x = tz; P_SetThingPosition(source); } } @@ -6101,7 +6110,7 @@ void P_RunShadows(void) if (dest->type == MT_THUNDERSHIELD) dest = dest->target; - P_TeleportMove(mobj, dest->x, dest->y, mobj->target->z); + P_SetOrigin(mobj, dest->x, dest->y, mobj->target->z); if (((mobj->eflags & MFE_VERTICALFLIP) && (mobj->ceilingz > mobj->z+mobj->height)) || (!(mobj->eflags & MFE_VERTICALFLIP) && (floorz < mobj->z))) @@ -6119,7 +6128,7 @@ void P_RunShadows(void) P_SetScale(mobj, FixedDiv(mobj->scale, max(FRACUNIT, ((mobj->target->z-mobj->z)/200)+FRACUNIT))); // Check new position to see if you should still be on that ledge - P_TeleportMove(mobj, dest->x, dest->y, mobj->z); + P_SetOrigin(mobj, dest->x, dest->y, mobj->z); mobj->z = (mobj->eflags & MFE_VERTICALFLIP ? mobj->ceilingz : floorz); @@ -7000,7 +7009,7 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target->eflags & MFE_VERTICALFLIP) z += mobj->target->height - FixedMul(mobj->target->scale, mobj->height); - P_TeleportMove(mobj, x, y, z); + P_MoveOrigin(mobj, x, y, z); } break; default: @@ -7400,7 +7409,7 @@ void P_MobjThinker(mobj_t *mobj) P_RemoveMobj(mobj); return; } - 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) @@ -8027,7 +8036,7 @@ void P_MobjThinker(mobj_t *mobj) } mobj->angle = mobj->target->angle; - P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, mobj->angle+ANGLE_180, mobj->target->radius), + P_MoveOrigin(mobj, mobj->target->x + P_ReturnThrustX(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->y + P_ReturnThrustY(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->z); P_SetScale(mobj, mobj->target->scale); @@ -8076,7 +8085,7 @@ void P_MobjThinker(mobj_t *mobj) P_RemoveMobj(mobj); return; } - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + P_SetOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); break; case MT_BRAKEDRIFT: if ((!mobj->target || !mobj->target->health || !mobj->target->player || !P_IsObjectOnGround(mobj->target)) @@ -8096,7 +8105,7 @@ void P_MobjThinker(mobj_t *mobj) newx = mobj->target->x + P_ReturnThrustX(mobj->target, travelangle+ANGLE_180, 24*mobj->target->scale); newy = mobj->target->y + P_ReturnThrustY(mobj->target, travelangle+ANGLE_180, 24*mobj->target->scale); - P_TeleportMove(mobj, newx, newy, mobj->target->z); + P_MoveOrigin(mobj, newx, newy, mobj->target->z); mobj->angle = travelangle - ((ANGLE_90/5)*mobj->target->player->kartstuff[k_drift]); P_SetScale(mobj, (mobj->destscale = mobj->target->scale)); @@ -8124,7 +8133,7 @@ void P_MobjThinker(mobj_t *mobj) P_RemoveMobj(mobj); return; } - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); break; case MT_INSTASHIELDB: mobj->flags2 ^= MF2_DONTDRAW; @@ -8137,7 +8146,7 @@ void P_MobjThinker(mobj_t *mobj) } K_MatchGenericExtraFlags(mobj, mobj->target); - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); break; case MT_BATTLEPOINT: if (!mobj->target || P_MobjWasRemoved(mobj->target)) @@ -8159,7 +8168,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->movefactor = mobj->target->height; } K_MatchGenericExtraFlags(mobj, mobj->target); - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height/2) + mobj->movefactor); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height/2) + mobj->movefactor); break; case MT_THUNDERSHIELD: { @@ -8194,7 +8203,7 @@ void P_MobjThinker(mobj_t *mobj) desty = mobj->target->y; } - P_TeleportMove(mobj, destx, desty, mobj->target->z); + P_MoveOrigin(mobj, destx, desty, mobj->target->z); break; } case MT_ROCKETSNEAKER: @@ -8230,7 +8239,7 @@ void P_MobjThinker(mobj_t *mobj) return; } - P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); mobj->angle = mobj->target->angle; mobj->scalespeed = mobj->target->scalespeed; mobj->destscale = mobj->target->destscale; @@ -8291,7 +8300,7 @@ void P_MobjThinker(mobj_t *mobj) if (cur->lastlook == 2 || cur->lastlook == 3) offy *= -1; - P_TeleportMove(cur, mobj->x + offx, mobj->y + offy, mobj->z); + P_MoveOrigin(cur, mobj->x + offx, mobj->y + offy, mobj->z); cur->scalespeed = mobj->target->scalespeed; cur->destscale = mobj->target->destscale; P_SetScale(cur, mobj->target->scale); @@ -8416,7 +8425,7 @@ void P_MobjThinker(mobj_t *mobj) while (cur && !P_MobjWasRemoved(cur)) { cur->angle += FixedAngle(mobj->info->speed); - P_TeleportMove(cur, mobj->x + FINECOSINE((cur->angle*8)>>ANGLETOFINESHIFT), + P_MoveOrigin(cur, mobj->x + FINECOSINE((cur->angle*8)>>ANGLETOFINESHIFT), mobj->y + FINESINE((cur->angle*8)>>ANGLETOFINESHIFT), mobj->z); //P_SpawnGhostMobj(cur)->tics = 2; @@ -8549,7 +8558,7 @@ void P_MobjThinker(mobj_t *mobj) fixed_t wz = mobj->tracer->z + (joint * ((mobj->z + (mobj->height/2)) - mobj->tracer->z) / (numjoints+1)); if (cur && !P_MobjWasRemoved(cur)) - P_TeleportMove(cur, wx, wy, wz); + P_MoveOrigin(cur, wx, wy, wz); else cur = P_SpawnMobj(wx, wy, wz, MT_FROGTONGUE_JOINT); @@ -8660,7 +8669,7 @@ void P_MobjThinker(mobj_t *mobj) continue; } else // Move into place - P_TeleportMove(cur, mobj->x, mobj->y, segz); + P_MoveOrigin(cur, mobj->x, mobj->y, segz); } else { @@ -8734,7 +8743,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->extravalue1 = 1; player->kartstuff[k_offroad] += 2<mo->x + P_ReturnThrustX(NULL, player->mo->angle, player->mo->radius) + P_ReturnThrustX(NULL, player->mo->angle+ANGLE_90, (mobj->threshold)<mo->y + P_ReturnThrustY(NULL, player->mo->angle, player->mo->radius) @@ -9855,7 +9864,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) cur = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SMK_ICEBLOCK_SIDE); P_SetTarget(&cur->target, mobj); cur->threshold = i; - P_TeleportMove(cur, cur->x + ((cur->radius>>FRACBITS) * FINECOSINE((FixedAngle((90*cur->threshold)<>ANGLETOFINESHIFT) & FINEMASK)), + P_MoveOrigin(cur, cur->x + ((cur->radius>>FRACBITS) * FINECOSINE((FixedAngle((90*cur->threshold)<>ANGLETOFINESHIFT) & FINEMASK)), cur->y + ((cur->radius>>FRACBITS) * FINESINE((FixedAngle((90*cur->threshold)<>ANGLETOFINESHIFT) & FINEMASK)), cur->z); cur->angle = ANGLE_90*(cur->threshold+1); @@ -9925,6 +9934,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; } @@ -10086,6 +10097,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; } @@ -10237,6 +10250,8 @@ void P_RemoveMobj(mobj_t *mobj) #endif P_RemoveThinker((thinker_t *)mobj); } + + R_RemoveMobjInterpolator(mobj); } // This does not need to be added to Lua. diff --git a/src/p_mobj.h b/src/p_mobj.h index 99ab0f0c..c321a770 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -274,6 +274,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; @@ -281,6 +283,8 @@ typedef struct mobj_s // More drawing info: to determine current sprite. angle_t angle; // orientation + angle_t old_angle; // orientation interpolation + angle_t old_angle2; spritenum_t sprite; // used to find patch_t and flip value UINT32 frame; // frame number, plus bits see p_pspr.h UINT16 anim_duration; // for FF_ANIMATE states @@ -356,6 +360,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; @@ -370,6 +376,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 // WARNING: New fields must be added separately to savegame and Lua. @@ -389,6 +396,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; @@ -396,6 +405,8 @@ typedef struct precipmobj_s // More drawing info: to determine current sprite. angle_t angle; // orientation + angle_t old_angle; // orientation interpolation + angle_t old_angle2; spritenum_t sprite; // used to find patch_t and flip value UINT32 frame; // frame number, plus bits see p_pspr.h UINT16 anim_duration; // for FF_ANIMATE states diff --git a/src/p_polyobj.c b/src/p_polyobj.c index cdcf37d9..fa98ad9a 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" @@ -2367,6 +2368,9 @@ INT32 EV_DoPolyObjRotate(polyrotdata_t *prdata) oldpo = po; + // interpolation + R_CreateInterpolator_Polyobj(&th->thinker, po); + th->turnobjs = prdata->turnobjs; // apply action to mirroring polyobjects as well @@ -2428,6 +2432,9 @@ INT32 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))) @@ -2443,12 +2450,14 @@ INT32 EV_DoPolyObjMove(polymovedata_t *pmdata) INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) { polyobj_t *po; + polyobj_t *oldpo; polywaypoint_t *th; mobj_t *mo2; mobj_t *first = NULL; mobj_t *last = NULL; mobj_t *target = NULL; thinker_t *wp; + INT32 start; if (!(po = Polyobj_GetForNum(pwdata->polyObjNum))) { @@ -2594,6 +2603,26 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata) return 0; } + // 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); + } + // Set pointnum th->pointnum = target->health; @@ -2645,6 +2674,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))) @@ -2685,6 +2717,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))) @@ -2756,6 +2791,9 @@ INT32 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))) @@ -2859,6 +2897,9 @@ INT32 EV_DoPolyObjFlag(line_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 f587c6e6..a63ef9e6 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -21,6 +21,7 @@ #include "p_local.h" #include "p_setup.h" #include "p_saveg.h" +#include "r_fps.h" #include "r_things.h" #include "r_state.h" #include "w_wad.h" @@ -2163,6 +2164,8 @@ static void LoadMobjThinker(actionf_p1 thinker) P_SetTarget(&waypointcap, mobj); mobj->info = (mobjinfo_t *)next; // temporarily, set when leave this function + + R_AddMobjInterpolator(mobj); } // diff --git a/src/p_setup.c b/src/p_setup.c index 5956a5f9..9506905f 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" @@ -30,6 +31,7 @@ #include "r_things.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" @@ -2909,7 +2911,10 @@ boolean P_SetupLevel(boolean skipprecip) { // 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(); @@ -2988,7 +2993,10 @@ boolean P_SetupLevel(boolean skipprecip) R_ClearLevelSplats(); #endif + R_InitializeLevelInterpolators(); + P_InitThinkers(); + R_InitMobjInterpolators(); P_InitCachedActions(); /// \note for not spawning precipitation, etc. when loading netgame snapshots @@ -3375,6 +3383,10 @@ boolean P_SetupLevel(boolean skipprecip) G_AddMapToBuffer(gamemap-1); + R_ResetViewInterpolation(); + R_ResetViewInterpolation(); + R_UpdateMobjInterpolators(); + return true; } diff --git a/src/p_spec.c b/src/p_spec.c index 671c49cf..9fb68792 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "p_setup.h" // levelflats for flat animation #include "r_data.h" +#include "r_fps.h" #include "m_random.h" #include "p_mobj.h" #include "i_system.h" @@ -2384,7 +2385,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) UINT8 i; 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); for (i = 0; i <= splitscreen; i++) { @@ -2846,7 +2847,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // Reset bot too. if (bot) { if (line->flags & ML_NOCLIMB) - 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; @@ -2890,7 +2891,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; @@ -5277,6 +5278,10 @@ static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline) floater->sector = sec; floater->vars[0] = tag; floater->sourceline = sourceline; + + // interpolation + R_CreateInterpolator_SectorPlane(&floater->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&floater->thinker, sec, true); } /** Adds a bridge thinker. @@ -5309,6 +5314,9 @@ static inline void P_AddBridgeThinker(line_t *sourceline, sector_t *sec) // Control sector tags should be End_Tag + (End_Tag - Start_Tag) bridge->vars[4] = sourceline->tag; // Start tag bridge->vars[5] = (sides[sourceline->sidenum[0]].textureoffset>>FRACBITS); // End tag + + // interpolation + R_CreateInterpolator_SectorPlane(&bridge->thinker, §ors[affectee], false); } */ @@ -5388,6 +5396,10 @@ static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline) - (sec->ceilingheight - sec->floorheight); raise->sourceline = sourceline; + + // interpolation + R_CreateInterpolator_SectorPlane(&raise->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&raise->thinker, sec, true); } // Function to maintain backwards compatibility @@ -5430,6 +5442,10 @@ static void P_AddOldAirbob(sector_t *sec, line_t *sourceline, boolean noadjust) - (sec->ceilingheight - sec->floorheight); airbob->sourceline = sourceline; + + // interpolation + R_CreateInterpolator_SectorPlane(&airbob->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&airbob->thinker, sec, true); } /** Adds a thwomp thinker. @@ -5471,6 +5487,11 @@ static inline void P_AddThwompThinker(sector_t *sec, sector_t *actionsector, lin thwomp->sourceline = sourceline; thwomp->sector->floordata = thwomp; thwomp->sector->ceilingdata = thwomp; + + // interpolation + R_CreateInterpolator_SectorPlane(&thwomp->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&thwomp->thinker, sec, true); + return; #undef speed #undef direction @@ -7036,6 +7057,22 @@ static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT3 s->last_height = sectors[control].floorheight + sectors[control].ceilingheight; s->affectee = affectee; P_AddThinker(&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; + } } /** Adds a wall scroller. diff --git a/src/p_telept.c b/src/p_telept.c index 74f9d462..41896562 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -128,7 +128,7 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle { UINT8 i; - if (!P_TeleportMove(thing, x, y, z)) + if (!P_SetOrigin(thing, x, y, z)) return false; thing->angle = angle; diff --git a/src/p_tick.c b/src/p_tick.c index 6a85027f..1964c1b9 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -23,6 +23,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "r_fps.h" // Object place #include "m_cheat.h" @@ -610,6 +611,8 @@ void P_Ticker(boolean run) if (run) { + R_UpdateMobjInterpolators(); + if (demo.recording) { G_WriteDemoExtraData(); @@ -796,6 +799,11 @@ void P_Ticker(boolean run) P_MoveChaseCamera(&players[displayplayers[i]], &camera[i], false); } + if (run) + { + R_UpdateLevelInterpolators(); + } + P_MapEnd(); if (demo.playback) diff --git a/src/p_user.c b/src/p_user.c index 8d553633..81129f09 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_things.h" @@ -1683,6 +1684,12 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) if (!(mobj->flags & MF_DONTENCOREMAP)) mobj->flags &= ~MF_DONTENCOREMAP; + // 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_frameangle2 : mobj->old_angle2); + return ghost; } @@ -9102,7 +9109,7 @@ void P_PlayerAfterThink(player_t *player) player->mo->momx = (player->mo->tracer->x - player->mo->x)*2; player->mo->momy = (player->mo->tracer->y - player->mo->y)*2; player->mo->momz = (player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2) - player->mo->z)*2; - P_TeleportMove(player->mo, player->mo->tracer->x, player->mo->tracer->y, player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2)); + P_MoveOrigin(player->mo, player->mo->tracer->x, player->mo->tracer->y, player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2)); player->pflags |= PF_JUMPED; player->secondjump = 0; player->pflags &= ~PF_THOKKED; diff --git a/src/r_bsp.c b/src/r_bsp.c index 6e35fddc..9a906a5f 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -831,6 +831,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) @@ -862,7 +863,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); @@ -919,7 +936,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 00000000..98e39bf6 --- /dev/null +++ b/src/r_fps.c @@ -0,0 +1,740 @@ +// 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 + +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 = {"fpscap", "300", CV_SAVE, fpscap_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL}; + +UINT32 R_GetFramerateCap(void) +{ + if (rendermode == render_none) + { + // If we're not rendering (dedicated server), + // we shouldn't be using any interpolation. + return TICRATE; + } + + if (cv_fpscap.value == 0) + { + // 0: Match refresh rate + return I_GetRefreshRate(); + } + + if (cv_fpscap.value < 0) + { + // -1: Unlimited + return 0; + } + + return cv_fpscap.value; +} + +boolean R_UsingFrameInterpolation(void) +{ + return (R_GetFramerateCap() != TICRATE || cv_timescale.value < FRACUNIT); +} + +static viewvars_t pview_old[MAXSPLITSCREENPLAYERS]; +static viewvars_t pview_new[MAXSPLITSCREENPLAYERS]; +static viewvars_t skyview_old[MAXSPLITSCREENPLAYERS]; +static viewvars_t skyview_new[MAXSPLITSCREENPLAYERS]; + +static viewvars_t *oldview = &pview_old[0]; +static int oldview_invalid = 0; +viewvars_t *newview = &pview_new[0]; + + +enum viewcontext_e viewcontext = VIEWCONTEXT_PLAYER1; + +static levelinterpolator_t **levelinterpolators; +static size_t levelinterpolators_len; +static size_t levelinterpolators_size; + + +static fixed_t R_LerpFixed(fixed_t from, fixed_t to, fixed_t frac) +{ + return 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) +{ + (void)player; + (void)skybox; + + // clip it in the case we are looking a hardware 90 degrees full aiming + // (lmps, network and use F12...) + if (rendermode == render_soft) + { + G_SoftwareClipAimingPitch((INT32 *)&aimingangle); + } + + centeryfrac = (viewheight/2)< FRACUNIT) + frac = FRACUNIT; + + if (oldview_invalid != 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; + + R_SetupFreelook(newview->player, skybox); +} + +void R_UpdateViewInterpolation(void) +{ + UINT8 i; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + pview_old[i] = pview_new[i]; + skyview_old[i] = skyview_new[i]; + } + + if (oldview_invalid) oldview_invalid--; +} + +void R_ResetViewInterpolation(void) +{ + oldview_invalid++; +} + +void R_SetViewContext(enum viewcontext_e _viewcontext) +{ + UINT8 i = 0; + + I_Assert(_viewcontext >= VIEWCONTEXT_PLAYER1 + && _viewcontext <= VIEWCONTEXT_SKY4); + viewcontext = _viewcontext; + + switch (viewcontext) + { + case VIEWCONTEXT_PLAYER1: + case VIEWCONTEXT_PLAYER2: + case VIEWCONTEXT_PLAYER3: + case VIEWCONTEXT_PLAYER4: + i = viewcontext - VIEWCONTEXT_PLAYER1; + oldview = &pview_old[i]; + newview = &pview_new[i]; + break; + case VIEWCONTEXT_SKY1: + case VIEWCONTEXT_SKY2: + case VIEWCONTEXT_SKY3: + case VIEWCONTEXT_SKY4: + i = viewcontext - VIEWCONTEXT_SKY1; + oldview = &skyview_old[i]; + newview = &skyview_new[i]; + 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->frameangle : mobj->angle; + 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->subsector = R_PointInSubsector(out->x, out->y); + + if (mobj->player) + { + out->angle = mobj->resetinterp ? mobj->player->frameangle : R_LerpAngle(mobj->player->old_frameangle, mobj->player->frameangle, 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; + 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->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_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_scale = mobj->scale; + + if (mobj->player) + { + mobj->player->old_frameangle2 = mobj->player->old_frameangle; + mobj->player->old_frameangle = mobj->player->frameangle; + } + + 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_x = mobj->x; + mobj->old_y = mobj->y; + mobj->old_z = mobj->z; + mobj->old_angle = mobj->angle; +} diff --git a/src/r_fps.h b/src/r_fps.h new file mode 100644 index 00000000..34a84eaf --- /dev/null +++ b/src/r_fps.h @@ -0,0 +1,158 @@ +// 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" + +extern consvar_t cv_fpscap; + +UINT32 R_GetFramerateCap(void); +boolean R_UsingFrameInterpolation(void); + +enum viewcontext_e +{ + VIEWCONTEXT_PLAYER1 = 0, + VIEWCONTEXT_PLAYER2, + VIEWCONTEXT_PLAYER3, + VIEWCONTEXT_PLAYER4, + VIEWCONTEXT_SKY1, + VIEWCONTEXT_SKY2, + VIEWCONTEXT_SKY3, + VIEWCONTEXT_SKY4 +}; + +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; +} 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(void); +// 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 a78cd57e..8dd4367f 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -31,6 +31,7 @@ #include "z_zone.h" #include "m_random.h" // quake camera shake #include "doomstat.h" // MAXSPLITSCREENPLAYERS +#include "r_fps.h" // Frame interpolation/uncapped #ifdef HWRENDER #include "hardware/hw_main.h" @@ -99,6 +100,10 @@ portal_pair *portal_base, *portal_cap; line_t *portalclipline; INT32 portalclipstart, portalclipend; +fixed_t rendertimefrac; +fixed_t renderdeltatics; +boolean renderisnewtic; + // // precalculated math tables // @@ -793,31 +798,6 @@ subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y) static mobj_t *viewmobj; -// 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(void) -{ - INT32 dy = 0; - - // 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 - || cv_grshearing.value -#endif - ) - G_SoftwareClipAimingPitch((INT32 *)&aimingangle); - - if (rendermode == render_soft) - { - dy = (AIMINGTODY(aimingangle)/fovtan) * viewwidth/BASEVIDWIDTH; - yslope = &yslopetab[viewheight*8 - (viewheight/2 + dy)]; - } - centery = (viewheight/2) + dy; - centeryfrac = centery<sky = true; viewmobj = skyboxmo[0]; #ifdef PARANOIA if (!viewmobj) @@ -847,24 +832,24 @@ 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 (/*!demo.playback && */player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle[0]; // WARNING: camera uses this - aimingangle = localaiming[0]; + newview->angle = localangle[0]; // WARNING: camera uses this + newview->aim = localaiming[0]; } else if (splitscreen) { @@ -872,27 +857,27 @@ void R_SkyboxFrame(player_t *player) { if (player == &players[displayplayers[i]]) { - viewangle = localangle[i]; - aimingangle = localaiming[i]; + newview->angle = localangle[i]; + newview->aim = localaiming[i]; break; } } } } } - viewangle += viewmobj->angle; + newview->angle += viewmobj->angle; - viewplayer = player; + newview->player = player; - viewx = viewmobj->x; - viewy = viewmobj->y; - viewz = 0; + newview->x = viewmobj->x; + newview->y = viewmobj->y; + newview->z = 0; if (viewmobj->spawnpoint) - viewz = ((fixed_t)viewmobj->spawnpoint->angle)<z = ((fixed_t)viewmobj->spawnpoint->angle)<x += quake.x; + newview->y += quake.y; + newview->z += quake.z; if (mapheaderinfo[gamemap-1]) { @@ -914,35 +899,35 @@ void R_SkyboxFrame(player_t *player) if (viewmobj->angle == 0) { - viewx += x; - viewy += y; + newview->x += x; + newview->y += y; } else if (viewmobj->angle == ANGLE_90) { - viewx -= y; - viewy += x; + newview->x -= y; + newview->y += x; } else if (viewmobj->angle == ANGLE_180) { - viewx -= x; - viewy -= y; + newview->x -= x; + newview->y -= y; } else if (viewmobj->angle == ANGLE_270) { - viewx += y; - viewy -= x; + newview->x += y; + newview->y -= x; } else { angle_t ang = 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 += (player->awayviewmobj->z + 20*FRACUNIT) / mh->skybox_scalez; + newview->z += (player->awayviewmobj->z + 20*FRACUNIT) / mh->skybox_scalez; else if (mh->skybox_scalez < 0) - viewz += (player->awayviewmobj->z + 20*FRACUNIT) * -mh->skybox_scalez; + newview->z += (player->awayviewmobj->z + 20*FRACUNIT) * -mh->skybox_scalez; } else if (thiscam->chase) { @@ -961,35 +946,35 @@ void R_SkyboxFrame(player_t *player) if (viewmobj->angle == 0) { - viewx += x; - viewy += y; + newview->x += x; + newview->y += y; } else if (viewmobj->angle == ANGLE_90) { - viewx -= y; - viewy += x; + newview->x -= y; + newview->y += x; } else if (viewmobj->angle == ANGLE_180) { - viewx -= x; - viewy -= y; + newview->x -= x; + newview->y -= y; } else if (viewmobj->angle == ANGLE_270) { - viewx += y; - viewy -= x; + newview->x += y; + newview->y -= x; } else { angle_t ang = 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 += (thiscam->z + (thiscam->height>>1)) / mh->skybox_scalez; + newview->z += (thiscam->z + (thiscam->height>>1)) / mh->skybox_scalez; else if (mh->skybox_scalez < 0) - viewz += (thiscam->z + (thiscam->height>>1)) * -mh->skybox_scalez; + newview->z += (thiscam->z + (thiscam->height>>1)) * -mh->skybox_scalez; } else { @@ -1007,47 +992,47 @@ void R_SkyboxFrame(player_t *player) if (viewmobj->angle == 0) { - viewx += x; - viewy += y; + newview->x += x; + newview->y += y; } else if (viewmobj->angle == ANGLE_90) { - viewx -= y; - viewy += x; + newview->x -= y; + newview->y += x; } else if (viewmobj->angle == ANGLE_180) { - viewx -= x; - viewy -= y; + newview->x -= x; + newview->y -= y; } else if (viewmobj->angle == ANGLE_270) { - viewx += y; - viewy -= x; + newview->x += y; + newview->y -= x; } else { angle_t ang = 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 += player->viewz / mh->skybox_scalez; + newview->z += player->viewz / mh->skybox_scalez; else if (mh->skybox_scalez < 0) - viewz += player->viewz * -mh->skybox_scalez; + newview->z += player->viewz * -mh->skybox_scalez; } } if (viewmobj->subsector) - viewsector = viewmobj->subsector->sector; + newview->sector = 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(); + R_InterpolateView(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); } void R_SetupFrame(player_t *player, boolean skybox) @@ -1059,21 +1044,25 @@ void R_SetupFrame(player_t *player, boolean skybox) { thiscam = &camera[3]; chasecam = (cv_chasecam4.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER4); } else if (splitscreen > 1 && player == &players[displayplayers[2]]) { thiscam = &camera[2]; chasecam = (cv_chasecam3.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER3); } else if (splitscreen && player == &players[displayplayers[1]]) { thiscam = &camera[1]; chasecam = (cv_chasecam2.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER2); } else { thiscam = &camera[0]; chasecam = (cv_chasecam.value != 0); + R_SetViewContext(VIEWCONTEXT_PLAYER1); } if (player->spectator) // no spectator chasecam @@ -1089,41 +1078,41 @@ void R_SetupFrame(player_t *player, boolean skybox) else if (!chasecam) thiscam->chase = false; - viewsky = !skybox; + newview->sky = !skybox; if (player->awayviewtics) { // cut-away view stuff viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN I_Assert(viewmobj != NULL); - viewz = viewmobj->z + 20*FRACUNIT; - aimingangle = player->awayviewaiming; - viewangle = viewmobj->angle; + newview->z = viewmobj->z + 20*FRACUNIT; + newview->aim = player->awayviewaiming; + newview->angle = viewmobj->angle; } else if (!player->spectator && chasecam) // use outside cam view { 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; viewmobj = player->mo; I_Assert(viewmobj != NULL); - aimingangle = player->aiming; - viewangle = viewmobj->angle; + newview->aim = player->aiming; + newview->angle = viewmobj->angle; if (!demo.playback && player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle[0]; // WARNING: camera uses this - aimingangle = localaiming[0]; + newview->angle = localangle[0]; // WARNING: camera uses this + newview->aim = localaiming[0]; } else if (splitscreen) { @@ -1132,47 +1121,47 @@ void R_SetupFrame(player_t *player, boolean skybox) { if (player == &players[displayplayers[i]]) { - viewangle = localangle[i]; - aimingangle = localaiming[i]; + newview->angle = localangle[i]; + newview->aim = localaiming[i]; break; } } } } } - 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 && thiscam->subsector->sector) - 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 = viewmobj->x; - viewy = viewmobj->y; - viewx += quake.x; - viewy += quake.y; + newview->x = viewmobj->x; + newview->y = viewmobj->y; + newview->x += quake.x; + newview->y += quake.y; if (viewmobj->subsector && thiscam->subsector->sector) - viewsector = viewmobj->subsector->sector; + newview->sector = 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(); + R_InterpolateView(R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT); } #define ANGLED_PORTALS @@ -1186,13 +1175,13 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal) #endif //R_SetupFrame(player, false); - viewx = portal->viewx; - viewy = portal->viewy; - viewz = portal->viewz; + newview->x = portal->viewx; + newview->y = portal->viewy; + newview->z = portal->viewz; - viewangle = portal->viewangle; - viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); - viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + newview->angle = portal->viewangle; + // newview->sin = FINESINE(newview->angle>>ANGLETOFINESHIFT); + // newview->cos = FINECOSINE(newview->angle>>ANGLETOFINESHIFT); portalcullsector = dest->frontsector; viewsector = dest->frontsector; @@ -1211,22 +1200,22 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal) dest_c.y = (dest->v1->y + dest->v2->y) / 2; // Heights! - viewz += dest->frontsector->floorheight - start->frontsector->floorheight; + newview->z += dest->frontsector->floorheight - start->frontsector->floorheight; // calculate the difference in position and rotation! #ifdef ANGLED_PORTALS if (dangle == 0) #endif { // the entrance goes straight opposite the exit, so we just need to mess with the offset. - viewx += dest_c.x - start_c.x; - viewy += dest_c.y - start_c.y; + newview->x += dest_c.x - start_c.x; + newview->y += dest_c.y - start_c.y; return; } #ifdef ANGLED_PORTALS - viewangle += dangle; - viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); - viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + newview->angle += dangle; + // newview->sin = FINESINE(newview->angle>>ANGLETOFINESHIFT); + // newview->cos = FINECOSINE(newview->angle>>ANGLETOFINESHIFT); //CONS_Printf("dangle == %u\n", AngleFixed(dangle)>>FRACBITS); // ???? @@ -1234,12 +1223,12 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal) fixed_t disttopoint; angle_t angtopoint; - disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy); - angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy); + disttopoint = R_PointToDist2(start_c.x, start_c.y, newview->x, newview->y); + angtopoint = R_PointToAngle2(start_c.x, start_c.y, newview->x, newview->y); angtopoint += dangle; - viewx = dest_c.x+FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); - viewy = dest_c.y+FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); + newview->x = dest_c.x+FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); + newview->y = dest_c.y+FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); } #endif } @@ -1544,4 +1533,7 @@ void R_RegisterEngineStuff(void) if (rendermode != render_soft && rendermode != render_none) HWR_AddCommands(); #endif + + // Frame interpolation/uncapped + CV_RegisterVar(&cv_fpscap); } diff --git a/src/r_main.h b/src/r_main.h index 38a58968..1ca1eb60 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -29,6 +29,13 @@ extern fixed_t projection, projectiony; 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 9cb35e19..c5308562 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -20,6 +20,7 @@ #include "z_zone.h" #include "m_misc.h" #include "i_video.h" // rendermode +#include "r_fps.h" #include "r_things.h" #include "r_plane.h" #include "p_tick.h" @@ -1158,13 +1159,28 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t gz, gzt; INT32 heightsec, phs; INT32 light = 0; - fixed_t this_scale = thing->scale; + fixed_t this_scale; fixed_t ang_scale = FRACUNIT; + // uncapped/interpolation + interpmobjstate_t interp = {0}; + + // do interpolation + if (R_UsingFrameInterpolation() && !paused) + { + R_InterpolateMobjState(thing, rendertimefrac, &interp); + } + else + { + R_InterpolateMobjState(thing, 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; gxt = FixedMul(tr_x, viewcos); gyt = -FixedMul(tr_y, viewsin); @@ -1229,10 +1245,7 @@ static void R_ProjectSprite(mobj_t *thing) if (sprframe->rotate != SRF_SINGLE || papersprite) { - if (thing->player) - ang = R_PointToAngle (thing->x, thing->y) - thing->player->frameangle; - else - ang = R_PointToAngle (thing->x, thing->y) - thing->angle; + ang = R_PointToAngle (interp.x, interp.y) - interp.angle; if (papersprite) ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT)); } @@ -1247,7 +1260,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 ((ang < ANGLE_180) && (sprframe->rotate & SRF_RIGHT)) // See from right rot = 6; // F7 slot @@ -1300,8 +1313,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); @@ -1343,7 +1356,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; } @@ -1353,12 +1366,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 = thing->z + thing->height - FixedMul(spritecachedinfo[lump].topoffset, this_scale); + gz = interp.z + thing->height - FixedMul(spritecachedinfo[lump].topoffset, this_scale); gzt = gz + FixedMul(spritecachedinfo[lump].height, this_scale); } else { - gzt = thing->z + FixedMul(spritecachedinfo[lump].topoffset, this_scale); + gzt = interp.z + FixedMul(spritecachedinfo[lump].topoffset, this_scale); gz = gzt - FixedMul(spritecachedinfo[lump].height, this_scale); } @@ -1374,7 +1387,7 @@ static void R_ProjectSprite(mobj_t *thing) light = thing->subsector->sector->numlights - 1; for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { - fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, thing->x, thing->y) + fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, interp.x, interp.y) : thing->subsector->sector->lightlist[lightnum].height; if (h <= gzt) { light = lightnum - 1; @@ -1416,12 +1429,12 @@ static void R_ProjectSprite(mobj_t *thing) vis->scale = yscale; //<sortscale = sortscale; vis->dispoffset = thing->info->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 = vis->gzt - viewz; vis->scalestep = scalestep; @@ -1539,9 +1552,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; gxt = FixedMul(tr_x, viewcos); gyt = -FixedMul(tr_y, viewsin); @@ -1610,7 +1636,7 @@ 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; } @@ -1626,7 +1652,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) //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) @@ -1639,12 +1665,12 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis = R_NewVisSprite(); vis->scale = vis->sortscale = yscale; //<dispoffset = 0; // Monster Iestyn: 23/11/15 - vis->gx = thing->x; - vis->gy = thing->y; + vis->gx = 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 4cb8bac5..0c4a7987 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" @@ -29,6 +30,8 @@ #include "d_clisrv.h" #include "f_finale.h" +// 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 @@ -397,46 +400,96 @@ 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 FPS_SAMPLE_RATE (50000) // How often to update FPS samples, in microseconds +#define NUM_FPS_SAMPLES 16 // Number of samples to store + +static double fps_samples[NUM_FPS_SAMPLES]; + +void SCR_CalculateFPS(void) +{ + static boolean init = false; + + static precise_t startTime = 0; + precise_t endTime = 0; + + static precise_t updateTime = 0; + int updateElapsed = 0; + int i; + + endTime = I_GetPreciseTime(); + + if (init == false) + { + startTime = updateTime = endTime; + init = true; + return; + } + + updateElapsed = (endTime - updateTime) / (I_GetPrecisePrecision() / 1000000); + + if (updateElapsed >= FPS_SAMPLE_RATE) + { + static int sampleIndex = 0; + int frameElapsed = (endTime - startTime) / (I_GetPrecisePrecision() / 1000000); + + fps_samples[sampleIndex] = frameElapsed / 1000.0f; + + sampleIndex++; + if (sampleIndex >= NUM_FPS_SAMPLES) + sampleIndex = 0; + + averageFPS = 0.0f; + for (i = 0; i < NUM_FPS_SAMPLES; i++) + { + averageFPS += fps_samples[i]; + } + averageFPS = 1000.0f / (averageFPS / NUM_FPS_SAMPLES); + + updateTime = endTime; + } + + startTime = endTime; +} void SCR_DisplayTicRate(void) { - tic_t i; - tic_t ontic = I_GetTime(); - tic_t totaltics = 0; const UINT8 *ticcntcolor = NULL; - - 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 = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SALMON, GTC_CACHE); - else if (totaltics == TICRATE) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MINT, GTC_CACHE); - - /*V_DrawString(vid.width-(24*vid.dupx), vid.height-(16*vid.dupy), - V_YELLOWMAP|V_NOSCALESTART, "FPS"); - V_DrawString(vid.width-(40*vid.dupx), vid.height-( 8*vid.dupy), - ticcntcolor|V_NOSCALESTART, va("%02d/%02u", totaltics, TICRATE));*/ + UINT32 cap = R_GetFramerateCap(); + UINT32 benchmark = (cap == 0) ? I_GetRefreshRate() : cap; + INT32 x = 318; + double fps = ceil(averageFPS); // draw "FPS" - V_DrawFixedPatch(306< (benchmark - 5)) + ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MINT, GTC_CACHE); + else if (fps < 20) + ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); + + if (cap != 0) + { + UINT32 digits = 1; + UINT32 c2 = cap; + + while (c2 > 0) + { + c2 = c2 / 10; + digits++; + } + + // draw total frame: + V_DrawPingNum(x, 190, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_HUDTRANS, cap, ticcntcolor); + x -= digits * 4; + + // draw "/" + V_DrawFixedPatch(x< 0) - 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 ba0f7f1d..c34065b6 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1351,12 +1351,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) @@ -1371,17 +1374,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; @@ -1398,27 +1404,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); } @@ -1428,6 +1429,7 @@ void I_FinishUpdate(void) OglSdlFinishUpdate(cv_vidwait.value); } #endif + exposevideo = SDL_FALSE; } @@ -1668,6 +1670,27 @@ INT32 VID_SetMode(INT32 modeNum) return SDL_TRUE; } +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; +} + static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) { int flags = 0; @@ -2057,4 +2080,15 @@ void I_ShutdownGraphics(void) SDL_QuitSubSystem(SDL_INIT_VIDEO); framebuffer = SDL_FALSE; } + +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; +} + #endif diff --git a/src/st_stuff.c b/src/st_stuff.c index 33394a81..d63c4ed0 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -42,8 +42,12 @@ #ifdef HAVE_BLUA #include "lua_hud.h" +#include "lua_hudlib_drawlist.h" +#include "lua_hook.h" #endif +#include "r_fps.h" + UINT16 objectsdrawn = 0; // @@ -175,6 +179,10 @@ hudinfo_t hudinfo[NUMHUDITEMS] = { 240, 160}, // HUD_LAP }; +#ifdef HAVE_BLUA +static huddrawlist_h luahuddrawlist_game; +#endif + // // STATUS BAR CODE // @@ -422,6 +430,10 @@ void ST_Init(void) return; ST_LoadGraphics(); + +#ifdef HAVE_BLUA + luahuddrawlist_game = LUA_HUD_CreateDrawList(); +#endif } // change the status bar too, when pressing F12 while viewing a demo. @@ -1952,7 +1964,14 @@ static void ST_overlayDrawer(void) #ifdef HAVE_BLUA if (!(netgame || multiplayer) || !hu_showscores) - LUAh_GameHUD(stplyr); + { + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_game); + LUAh_GameHUD(stplyr, luahuddrawlist_game); + } + LUA_HUD_DrawList(luahuddrawlist_game); + } #endif // draw level title Tails diff --git a/src/w_wad.c b/src/w_wad.c index d4da027b..174cd940 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -55,6 +55,7 @@ #include "g_game.h" // G_LoadGameData #include "filesrch.h" +#include "i_time.h" #include "i_video.h" // rendermode #include "d_netfil.h" #include "dehacked.h" diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index b32ee617..6fbf4f71 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -35,6 +35,7 @@ #include #include "../m_misc.h" +#include "../i_time.h" #include "../i_video.h" #include "../i_sound.h" #include "../i_system.h" @@ -45,6 +46,7 @@ #include "../d_main.h" #include "../m_argv.h" +#include "../m_fixed.h" #include "../w_wad.h" #include "../z_zone.h" @@ -259,10 +261,25 @@ tic_t I_GetTime(void) return newtics; } -void I_Sleep(void) +precise_t I_GetPreciseTime(void) { - if (cv_sleep.value > 0) - Sleep(cv_sleep.value); + LARGE_INTEGER time; + BOOL res = QueryPerformanceCounter(&time); + if (!res) I_Error("QueryPerformanceCounter error"); // if this happens, you've gone back to the 90s + return (precise_t) time.QuadPart; +} + +UINT64 I_GetPrecisePrecision(void) +{ + LARGE_INTEGER time; + BOOL res = QueryPerformanceFrequency(&time); + if (!res) I_Error("QueryPerformanceFrequency error"); // if this happens, you've gone back to the 90s + return (precise_t) time.QuadPart; +} + +void I_Sleep(UINT32 ms) +{ + Sleep(ms); } // should move to i_video