diff --git a/src/d_main.c b/src/d_main.c index 2f564c6c8..963439d66 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -477,6 +477,7 @@ static void D_Display(void) if (!automapactive && !dedicated && cv_renderview.value) { + R_ApplyLevelInterpolators(cv_frameinterpolation.value == 1 ? rendertimefrac : FRACUNIT); PS_START_TIMING(ps_rendercalltime); if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) { @@ -525,6 +526,7 @@ static void D_Display(void) V_DoPostProcessor(1, postimgtype2, postimgparam2); } PS_STOP_TIMING(ps_rendercalltime); + R_RestoreLevelInterpolators(); } if (lastdraw) diff --git a/src/g_game.c b/src/g_game.c index 8eb731b1f..6d263e302 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1830,6 +1830,7 @@ void G_DoLoadLevel(boolean resetplayer) } // Setup the level. + R_InitializeLevelInterpolators(); if (!P_LoadLevel(false, false)) // this never returns false? { // fail so reset game stuff diff --git a/src/p_ceilng.c b/src/p_ceilng.c index 50344ee0c..f1cca0825 100644 --- a/src/p_ceilng.c +++ b/src/p_ceilng.c @@ -13,6 +13,7 @@ #include "doomdef.h" #include "p_local.h" +#include "r_fps.h" #include "r_main.h" #include "s_sound.h" #include "z_zone.h" @@ -602,6 +603,9 @@ INT32 EV_DoCeiling(line_t *line, ceiling_e type) ceiling->tag = tag; ceiling->type = type; firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } @@ -679,6 +683,10 @@ INT32 EV_DoCrush(line_t *line, ceiling_e type) ceiling->tag = tag; ceiling->type = type; + + // interpolation + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&ceiling->thinker, sec, true); } return rtn; } diff --git a/src/p_floor.c b/src/p_floor.c index 5536ee913..5fcefd203 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -16,6 +16,7 @@ #include "m_random.h" #include "p_local.h" #include "p_slopes.h" +#include "r_fps.h" #include "r_state.h" #include "s_sound.h" #include "z_zone.h" @@ -573,6 +574,8 @@ void T_ContinuousFalling(continuousfall_t *faller) { faller->sector->ceilingheight = faller->ceilingstartheight; faller->sector->floorheight = faller->floorstartheight; + + R_ClearLevelInterpolatorState(&faller->thinker); } P_CheckSector(faller->sector, false); // you might think this is irrelevant. you would be wrong @@ -2010,6 +2013,9 @@ void EV_DoFloor(line_t *line, floor_e floortype) } firstone = 0; + + // interpolation + R_CreateInterpolator_SectorPlane(&dofloor->thinker, sec, false); } } @@ -2140,6 +2146,10 @@ void EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed) default: break; } + + // interpolation + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&elevator->thinker, sec, true); } } @@ -2318,6 +2328,10 @@ void EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boole faller->destheight = backwards ? backsector->ceilingheight : backsector->floorheight; faller->direction = backwards ? 1 : -1; + + // interpolation + R_CreateInterpolator_SectorPlane(&faller->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&faller->thinker, sec, true); } // Some other 3dfloor special things Tails 03-11-2002 (Search p_mobj.c for description) diff --git a/src/p_spec.c b/src/p_spec.c index 3fe77d7a2..f6a0ce606 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "p_setup.h" // levelflats for flat animation #include "r_data.h" +#include "r_fps.h" #include "r_textures.h" #include "m_random.h" #include "p_mobj.h" @@ -5695,6 +5696,9 @@ static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, displace->speed = speed; displace->type = type; displace->reverse = reverse; + + // interpolation + R_CreateInterpolator_SectorPlane(&displace->thinker, §ors[affectee], false); } /** Adds a Mario block thinker, which changes the block's texture between blank @@ -5754,6 +5758,10 @@ static void P_AddRaiseThinker(sector_t *sec, INT16 tag, fixed_t speed, fixed_t c raise->flags |= RF_REVERSE; if (spindash) raise->flags |= RF_SPINDASH; + + // interpolation + R_CreateInterpolator_SectorPlane(&raise->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&raise->thinker, sec, true); } static void P_AddAirbob(sector_t *sec, INT16 tag, fixed_t dist, boolean raise, boolean spindash, boolean dynamic) @@ -5779,6 +5787,10 @@ static void P_AddAirbob(sector_t *sec, INT16 tag, fixed_t dist, boolean raise, b airbob->flags |= RF_SPINDASH; if (dynamic) airbob->flags |= RF_DYNAMIC; + + // interpolation + R_CreateInterpolator_SectorPlane(&airbob->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&airbob->thinker, sec, true); } /** Adds a thwomp thinker. @@ -5819,6 +5831,10 @@ static inline void P_AddThwompThinker(sector_t *sec, line_t *sourceline, fixed_t sec->ceilingdata = thwomp; // Start with 'resting' texture sides[sourceline->sidenum[0]].midtexture = sides[sourceline->sidenum[0]].bottomtexture; + + // interpolation + R_CreateInterpolator_SectorPlane(&thwomp->thinker, sec, false); + R_CreateInterpolator_SectorPlane(&thwomp->thinker, sec, true); } /** Adds a thinker which checks if any MF_ENEMY objects with health are in the defined area. diff --git a/src/p_tick.c b/src/p_tick.c index ca70fb926..c2f2b0579 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -764,6 +764,11 @@ void P_Ticker(boolean run) LUA_HOOK(PostThinkFrame); } + if (run) + { + R_UpdateLevelInterpolators(); + } + P_MapEnd(); // Z_CheckMemCleanup(); diff --git a/src/r_fps.c b/src/r_fps.c index 9bbae0676..b69531574 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -20,6 +20,7 @@ #include "r_plane.h" #include "p_spec.h" #include "r_state.h" +#include "z_zone.h" #ifdef HWRENDER #include "hardware/hw_main.h" // for cv_glshearing #endif @@ -40,6 +41,11 @@ viewvars_t *newview = &p1view_new; enum viewcontext_e viewcontext = VIEWCONTEXT_PLAYER1; +static levelinterpolator_t **levelinterpolators; +static size_t levelinterpolators_len; +static size_t levelinterpolators_size; + + static fixed_t R_LerpFixed(fixed_t from, fixed_t to, fixed_t frac) { return from + FixedMul(frac, to - from); @@ -194,3 +200,155 @@ void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjst out->z = R_LerpFixed(mobj->old_z, mobj->z, frac); out->angle = mobj->angle; } + +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_InitializeLevelInterpolators(void) +{ + levelinterpolators_len = 0; + levelinterpolators_size = 0; + levelinterpolators = NULL; +} + +static void UpdateLevelInterpolatorState(levelinterpolator_t *interp) +{ + 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; + } +} + +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; + + 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); + } + } + } +} + +void R_RestoreLevelInterpolators(void) +{ + size_t i; + + 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; + } + } + } +} diff --git a/src/r_fps.h b/src/r_fps.h index 04f44de6e..df506fb56 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -51,6 +51,27 @@ typedef struct { angle_t angle; } interpmobjstate_t; +// Level interpolators + +// The union tag for levelinterpolator_t +typedef enum { + LVLINTERP_SectorPlane, +} 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; + }; +} 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. @@ -64,4 +85,17 @@ 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); + +// 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); + #endif