diff --git a/src/p_map.c b/src/p_map.c
index fe67ad49c719bc0a3de204ea6458a4bbaae1d16c..e9d289e2d0710b6141ff1b867eeedffc380ca239 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -19,6 +19,7 @@
 #include "m_random.h"
 #include "p_local.h"
 #include "p_setup.h" // NiGHTS stuff
+#include "r_fps.h"
 #include "r_state.h"
 #include "r_main.h"
 #include "r_sky.h"
@@ -103,7 +104,7 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
 	thing->floorrover = tmfloorrover;
 	thing->ceilingrover = tmceilingrover;
 
-	P_ResetMobjInterpolationState(thing);
+	R_ResetMobjInterpolationState(thing);
 
 	return true;
 }
diff --git a/src/p_mobj.c b/src/p_mobj.c
index cda1c3cf7947037fc182b9c5ec11d8991817d8ad..eb939c9684a01af375d88a922834c91d8fefb16a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -18,6 +18,7 @@
 #include "hu_stuff.h"
 #include "p_local.h"
 #include "p_setup.h"
+#include "r_fps.h"
 #include "r_main.h"
 #include "r_skins.h"
 #include "r_sky.h"
@@ -42,79 +43,6 @@ actioncache_t actioncachehead;
 
 static mobj_t *overlaycap = NULL;
 
-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 P_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;
-
-	P_ResetMobjInterpolationState(mobj);
-}
-
-static void RemoveInterpolatedMobj(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 P_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 P_UpdateMobjInterpolators(void)
-{
-	size_t i;
-	for (i = 0; i < interpolated_mobjs_len; i++)
-	{
-		mobj_t *mobj = interpolated_mobjs[i];
-		if (!P_MobjWasRemoved(mobj))
-			P_ResetMobjInterpolationState(mobj);
-	}
-}
-
 void P_InitCachedActions(void)
 {
 	actioncachehead.prev = actioncachehead.next = &actioncachehead;
@@ -965,39 +893,6 @@ void P_EmeraldManager(void)
 	emeraldspawndelay = 0;
 }
 
-//
-// P_ResetMobjInterpolationState
-//
-// Reset the rendering interpolation state of the mobj.
-//
-void P_ResetMobjInterpolationState(mobj_t *mobj)
-{
-	mobj->old_x = mobj->x;
-	mobj->old_y = mobj->y;
-	mobj->old_z = mobj->z;
-	mobj->old_angle = mobj->angle;
-	mobj->old_pitch = mobj->pitch;
-	mobj->old_roll = mobj->roll;
-
-	if (mobj->player)
-	{
-		mobj->player->old_drawangle = mobj->player->drawangle;
-	}
-}
-
-//
-// P_ResetPrecipitationMobjInterpolationState
-//
-// Reset the rendering interpolation state of the precipmobj.
-//
-void P_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj)
-{
-	mobj->old_x = mobj->x;
-	mobj->old_y = mobj->y;
-	mobj->old_z = mobj->z;
-	mobj->old_angle = mobj->angle;
-}
-
 //
 // P_ExplodeMissile
 //
@@ -4132,7 +4027,7 @@ void P_NullPrecipThinker(precipmobj_t *mobj)
 
 void P_SnowThinker(precipmobj_t *mobj)
 {
-	P_ResetPrecipitationMobjInterpolationState(mobj);
+	R_ResetPrecipitationMobjInterpolationState(mobj);
 
 	P_CycleStateAnimation((mobj_t *)mobj);
 
@@ -4140,13 +4035,13 @@ void P_SnowThinker(precipmobj_t *mobj)
 	if ((mobj->z += mobj->momz) <= mobj->floorz)
 	{
 		mobj->z = mobj->ceilingz;
-		P_ResetPrecipitationMobjInterpolationState(mobj);
+		R_ResetPrecipitationMobjInterpolationState(mobj);
 	}
 }
 
 void P_RainThinker(precipmobj_t *mobj)
 {
-	P_ResetPrecipitationMobjInterpolationState(mobj);
+	R_ResetPrecipitationMobjInterpolationState(mobj);
 
 	P_CycleStateAnimation((mobj_t *)mobj);
 
@@ -4167,7 +4062,7 @@ void P_RainThinker(precipmobj_t *mobj)
 			return;
 
 		mobj->z = mobj->ceilingz;
-		P_ResetPrecipitationMobjInterpolationState(mobj);
+		R_ResetPrecipitationMobjInterpolationState(mobj);
 		P_SetPrecipMobjState(mobj, S_RAIN1);
 
 		return;
@@ -11010,7 +10905,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	if (CheckForReverseGravity && !(mobj->flags & MF_NOBLOCKMAP))
 		P_CheckGravity(mobj, false);
 
-	P_AddMobjInterpolator(mobj);
+	R_AddMobjInterpolator(mobj);
 
 	return mobj;
 }
@@ -11059,7 +10954,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
 	 || mobj->subsector->sector->floorpic == skyflatnum)
 		mobj->precipflags |= PCF_PIT;
 
-	P_ResetPrecipitationMobjInterpolationState(mobj);
+	R_ResetPrecipitationMobjInterpolationState(mobj);
 
 	return mobj;
 }
@@ -11180,7 +11075,7 @@ void P_RemoveMobj(mobj_t *mobj)
 	memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
 #endif
 
-	RemoveInterpolatedMobj(mobj);
+	R_RemoveMobjInterpolator(mobj);
 
 	// free block
 	if (!mobj->thinker.next)
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 9b5a215a2e8199192203edfa3336bfa00d9fffbf..e4c57beecb8302984b5e136c37fcb48398d10e36 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -504,14 +504,6 @@ boolean P_SceneryZMovement(mobj_t *mo);
 void P_PlayerZMovement(mobj_t *mo);
 void P_EmeraldManager(void);
 
-// Initialize internal mobj interpolator list (e.g. during level loading)
-void P_InitMobjInterpolators(void);
-// Add interpolation state for the given mobj
-void P_AddMobjInterpolator(mobj_t *mobj);
-void P_UpdateMobjInterpolators(void);
-void P_ResetMobjInterpolationState(mobj_t *mobj);
-void P_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj);
-
 extern INT32 modulothing;
 
 #define MAXHUNTEMERALDS 64
diff --git a/src/p_saveg.c b/src/p_saveg.c
index eed5941988b657aa60f838401973bfc908673dc2..d493ae0138e29ad5ce3d9d64a98aae61a9b9e3ea 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -22,6 +22,7 @@
 #include "p_setup.h"
 #include "p_saveg.h"
 #include "r_data.h"
+#include "r_fps.h"
 #include "r_textures.h"
 #include "r_things.h"
 #include "r_skins.h"
@@ -2969,7 +2970,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 
 	mobj->info = (mobjinfo_t *)next; // temporarily, set when leave this function
 
-	P_AddMobjInterpolator(mobj);
+	R_AddMobjInterpolator(mobj);
 
 	return &mobj->thinker;
 }
diff --git a/src/p_setup.c b/src/p_setup.c
index 063c1b975ef8e9fba00e5c2a50332493231d35a3..0e7432bbf81dc03049dfc3a792905f4917efd4ea 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -4376,7 +4376,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	R_InitializeLevelInterpolators();
 
 	P_InitThinkers();
-	P_InitMobjInterpolators();
+	R_InitMobjInterpolators();
 	P_InitCachedActions();
 
 	if (!fromnetsave && savedata.lives > 0)
diff --git a/src/p_tick.c b/src/p_tick.c
index 7773dec553b4db7d784928a428c4117733ecdbdd..a388ab3f20ac5c4bf89c67cbcb2288c981bdf996 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -642,7 +642,7 @@ void P_Ticker(boolean run)
 
 	if (run)
 	{
-		P_UpdateMobjInterpolators();
+		R_UpdateMobjInterpolators();
 
 		if (demorecording)
 			G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0);
diff --git a/src/r_fps.c b/src/r_fps.c
index 136cdfe9e73d2abe7293079c89216d17d072a2ac..5c0c8f6df853bfe3f3ad749b17edf09fdacfe293 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -556,3 +556,109 @@ void R_DestroyLevelInterpolators(thinker_t *thinker)
 		}
 	}
 }
+
+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);
+}
+
+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_x = mobj->x;
+	mobj->old_y = mobj->y;
+	mobj->old_z = mobj->z;
+	mobj->old_angle = mobj->angle;
+	mobj->old_pitch = mobj->pitch;
+	mobj->old_roll = mobj->roll;
+
+	if (mobj->player)
+	{
+		mobj->player->old_drawangle = mobj->player->drawangle;
+	}
+}
+
+//
+// P_ResetPrecipitationMobjInterpolationState
+//
+// Reset the rendering interpolation state of the precipmobj.
+//
+void R_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj)
+{
+	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
index 75d9ead3d74e50e130b7bb95c825ae92e9e0d9b5..ec85a1f35fafa5c7ee96eeb24f28279bd0d1c00b 100644
--- a/src/r_fps.h
+++ b/src/r_fps.h
@@ -133,4 +133,14 @@ 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