diff --git a/src/p_setup.c b/src/p_setup.c
index 79c919a4fce145999a1154c3a8c67d44a80782dc..565a44eaeb257f36ed156005f30750158223168f 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -978,11 +978,6 @@ static void P_LoadVertices(UINT8 *data)
 	}
 }
 
-static void InitializeSectorPortal(sectorportal_t *secportal)
-{
-	memset(secportal, 0, sizeof(*secportal));
-}
-
 static void P_InitializeSector(sector_t *ss)
 {
 	memset(&ss->soundorg, 0, sizeof(ss->soundorg));
@@ -996,8 +991,8 @@ static void P_InitializeSector(sector_t *ss)
 	ss->lightingdata = NULL;
 	ss->fadecolormapdata = NULL;
 
-	InitializeSectorPortal(&ss->portal_floor);
-	InitializeSectorPortal(&ss->portal_ceiling);
+	ss->portal_floor = UINT32_MAX;
+	ss->portal_ceiling = UINT32_MAX;
 
 	ss->heightsec = -1;
 	ss->camsec = -1;
@@ -7827,6 +7822,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	P_InitThinkers();
 	R_InitMobjInterpolators();
 	P_InitCachedActions();
+	P_InitSectorPortals();
 
 	// internal game map
 	maplumpname = G_BuildMapName(gamemap);
diff --git a/src/p_spec.c b/src/p_spec.c
index ccd72e594c75e0d209ce5090f31834bb833a839f..0c798b97645f4211bf9a603da4c8453f06c429e6 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -52,6 +52,10 @@ mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint
 mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs
 mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
 
+size_t secportalcount;
+size_t secportalcapacity;
+sectorportal_t *secportals;
+
 /** Animated texture descriptor
   * This keeps track of an animated texture or an animated flat.
   * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t
@@ -6186,8 +6190,33 @@ fixed_t P_GetSectorGravityFactor(sector_t *sec)
 		return sec->gravity;
 }
 
+void P_InitSectorPortals(void)
+{
+	secportalcount = 0;
+	secportalcapacity = 0;
+	secportals = NULL;
+}
+
+UINT32 P_NewSectorPortal(void)
+{
+	size_t i = secportalcount++;
+	if (i == UINT32_MAX)
+		I_Error("Too many sector portals");
+
+	if (secportalcapacity == 0 || secportalcount == secportalcapacity)
+	{
+		secportalcapacity = secportalcapacity ? (secportalcapacity * 2) : 16;
+		secportals = Z_Realloc(secportals, secportalcapacity * sizeof(sectorportal_t), PU_LEVEL, NULL);
+	}
+
+	return (UINT32)i;
+}
+
 static boolean P_IsSectorPortalValid(sectorportal_t *secportal)
 {
+	if (secportal == NULL)
+		return false;
+
 	switch (secportal->type)
 	{
 	case SECPORTAL_LINE:
@@ -6208,14 +6237,32 @@ boolean P_SectorHasPortal(sector_t *sector)
 	return P_SectorHasFloorPortal(sector) || P_SectorHasCeilingPortal(sector);
 }
 
+sectorportal_t *P_SectorGetFloorPortal(sector_t *sector)
+{
+	UINT32 num = sector->portal_floor;
+	if (num >= secportalcount)
+		return NULL;
+
+	return &secportals[num];
+}
+
+sectorportal_t *P_SectorGetCeilingPortal(sector_t *sector)
+{
+	UINT32 num = sector->portal_ceiling;
+	if (num >= secportalcount)
+		return NULL;
+
+	return &secportals[num];
+}
+
 boolean P_SectorHasFloorPortal(sector_t *sector)
 {
-	return P_IsSectorPortalValid(&sector->portal_floor);
+	return P_IsSectorPortalValid(P_SectorGetFloorPortal(sector));
 }
 
 boolean P_SectorHasCeilingPortal(sector_t *sector)
 {
-	return P_IsSectorPortalValid(&sector->portal_ceiling);
+	return P_IsSectorPortalValid(P_SectorGetCeilingPortal(sector));
 }
 
 boolean P_CompareSectorPortals(sectorportal_t *a, sectorportal_t *b)
@@ -6260,25 +6307,41 @@ static mobj_t *P_GetMobjByTag(INT32 tag)
 	return NULL;
 }
 
-static void P_CopySectorPortal(sectorportal_t *dest, sectorportal_t *src)
+static void P_DoPortalCopyFromLine(sector_t *dest_sector, int plane_type, int tag)
 {
-	if (src->type == SECPORTAL_NONE)
-		return;
+	INT32 secnum = -1;
+	TAG_ITER_SECTORS(tag, secnum)
+	{
+		sector_t *src_sector = &sectors[secnum];
+		if (plane_type == TMP_FLOOR || plane_type == TMP_BOTH)
+			dest_sector->portal_floor = src_sector->portal_floor;
+		if (plane_type == TMP_CEILING || plane_type == TMP_BOTH)
+			dest_sector->portal_ceiling = src_sector->portal_ceiling;
+	}
+}
+
+static sectorportal_t *P_SectorGetFloorPortalOrCreate(sector_t *sector)
+{
+	sectorportal_t *secportal = P_SectorGetFloorPortal(sector);
+	if (secportal == NULL)
+	{
+		sector->portal_floor = P_NewSectorPortal();
+		return &secportals[sector->portal_floor];
+	}
 
-	memcpy(dest, src, sizeof(sectorportal_t));
+	return secportal;
 }
 
-static void P_DoPortalCopyFromLine(sectorportal_t *floorportal, sectorportal_t *ceilportal, int tag)
+static sectorportal_t *P_SectorGetCeilingPortalOrCreate(sector_t *sector)
 {
-	INT32 secnum = -1;
-	TAG_ITER_SECTORS(tag, secnum)
+	sectorportal_t *secportal = P_SectorGetCeilingPortal(sector);
+	if (secportal == NULL)
 	{
-		sector_t *src_sector = &sectors[secnum];
-		if (floorportal)
-			P_CopySectorPortal(floorportal, &src_sector->portal_floor);
-		if (ceilportal)
-			P_CopySectorPortal(ceilportal, &src_sector->portal_ceiling);
+		sector->portal_ceiling = P_NewSectorPortal();
+		return &secportals[sector->portal_ceiling];
 	}
+
+	return secportal;
 }
 
 /** After the map has loaded, scans for specials that spawn 3Dfloors and
@@ -6466,8 +6529,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 
 				TAG_ITER_SECTORS(target_sector_tag, s1) // Target sector tag
 				{
-					sectorportal_t *floorportal = &sectors[s1].portal_floor;
-					sectorportal_t *ceilportal = &sectors[s1].portal_ceiling;
+					sector_t *target_sector = &sectors[s1];
 
 					// Line portal
 					if (portal_type == 0)
@@ -6483,12 +6545,14 @@ void P_SpawnSpecials(boolean fromnetsave)
 							{
 								if (floor)
 								{
+									sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector);
 									floorportal->type = SECPORTAL_LINE;
 									floorportal->line.start = &lines[i];
 									floorportal->line.dest = &lines[linenum];
 								}
 								if (ceiling)
 								{
+									sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector);
 									ceilportal->type = SECPORTAL_LINE;
 									ceilportal->line.start = &lines[i];
 									ceilportal->line.dest = &lines[linenum];
@@ -6500,9 +6564,15 @@ void P_SpawnSpecials(boolean fromnetsave)
 					else if (portal_type == 2)
 					{
 						if (floor)
+						{
+							sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector);
 							floorportal->type = SECPORTAL_SKYBOX;
+						}
 						if (ceiling)
+						{
+							sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector);
 							ceilportal->type = SECPORTAL_SKYBOX;
+						}
 					}
 					// Plane portal
 					else if (portal_type == 7)
@@ -6510,16 +6580,18 @@ void P_SpawnSpecials(boolean fromnetsave)
 						INT32 s2 = -1;
 						TAG_ITER_SECTORS(misc, s2) // Sector tag to make a portal to
 						{
-							sector_t *target_sector = &sectors[s2];
+							sector_t *view_sector = &sectors[s2];
 							if (floor)
 							{
+								sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector);
 								floorportal->type = SECPORTAL_CEILING;
-								floorportal->sector = target_sector;
+								floorportal->sector = view_sector;
 							}
 							if (ceiling)
 							{
+								sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector);
 								ceilportal->type = SECPORTAL_FLOOR;
-								ceilportal->sector = target_sector;
+								ceilportal->sector = view_sector;
 							}
 						}
 					}
@@ -6531,11 +6603,13 @@ void P_SpawnSpecials(boolean fromnetsave)
 							break;
 						if (floor)
 						{
+							sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector);
 							floorportal->type = SECPORTAL_OBJECT;
 							floorportal->mobj = mobj;
 						}
 						if (ceiling)
 						{
+							sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector);
 							ceilportal->type = SECPORTAL_OBJECT;
 							ceilportal->mobj = mobj;
 						}
@@ -7361,30 +7435,16 @@ void P_SpawnSpecials(boolean fromnetsave)
 		int plane_type = lines[i].args[2];
 		int tag_to_copy = lines[i].args[3];
 
-		boolean floor, ceiling;
-		if (plane_type == TMP_BOTH || plane_type == 3)
-			floor = ceiling = true;
-		else
-		{
-			floor = plane_type == TMP_FLOOR;
-			ceiling = plane_type == TMP_CEILING;
-		}
+		if (plane_type == 3)
+			plane_type = TMP_BOTH;
 
 		if (target_sector_tag == 0)
-		{
-			sectorportal_t *floorportal = floor ? &lines[i].frontsector->portal_floor : NULL;
-			sectorportal_t *ceilportal = ceiling ? &lines[i].frontsector->portal_ceiling : NULL;
-			P_DoPortalCopyFromLine(floorportal, ceilportal, tag_to_copy);
-		}
+			P_DoPortalCopyFromLine(lines[i].frontsector, plane_type, tag_to_copy);
 		else
 		{
 			INT32 s1 = -1;
 			TAG_ITER_SECTORS(target_sector_tag, s1)
-			{
-				sectorportal_t *floorportal = floor ? &sectors[s1].portal_floor : NULL;
-				sectorportal_t *ceilportal = ceiling ? &sectors[s1].portal_ceiling : NULL;
-				P_DoPortalCopyFromLine(floorportal, ceilportal, tag_to_copy);
-			}
+				P_DoPortalCopyFromLine(&sectors[s1], plane_type, tag_to_copy);
 		}
 		break;
 	}
diff --git a/src/p_spec.h b/src/p_spec.h
index 0c4ce4f3296620bd4ed6baf30a25754bb1af73e1..bb995a97a11fa8b212f4c04fd6a9c2d69c6fab27 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -21,6 +21,10 @@ extern mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpo
 extern mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs
 extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
 
+extern size_t secportalcount;
+extern size_t secportalcapacity;
+extern sectorportal_t *secportals;
+
 // Amount (dx, dy) vector linedef is shifted right to get scroll amount
 #define SCROLL_SHIFT 5
 
@@ -521,6 +525,12 @@ INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max);
 void P_SetupSignExit(player_t *player);
 boolean P_IsFlagAtBase(mobjtype_t flag);
 
+void P_InitSectorPortals(void);
+UINT32 P_NewSectorPortal(void);
+
+sectorportal_t *P_SectorGetFloorPortal(sector_t *sector);
+sectorportal_t *P_SectorGetCeilingPortal(sector_t *sector);
+
 boolean P_SectorHasPortal(sector_t *sector);
 boolean P_SectorHasFloorPortal(sector_t *sector);
 boolean P_SectorHasCeilingPortal(sector_t *sector);
diff --git a/src/r_bsp.c b/src/r_bsp.c
index 6f115a923e0a96bcb6a82d6d7f5cbc20f133f59d..8ab5998c44830f23e56bf06992ae380243bb64cd 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -923,7 +923,7 @@ static void R_Subsector(size_t num)
 		|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum))
 	{
 		floorplane = R_FindPlane(frontsector, frontsector->floorheight, frontsector->floorpic, floorlightlevel,
-			frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorangle, floorcolormap, NULL, NULL, frontsector->f_slope, P_SectorHasFloorPortal(frontsector) ? &frontsector->portal_floor : NULL);
+			frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorangle, floorcolormap, NULL, NULL, frontsector->f_slope, P_SectorGetFloorPortal(frontsector));
 	}
 	else
 		floorplane = NULL;
@@ -935,7 +935,7 @@ static void R_Subsector(size_t num)
 	{
 		ceilingplane = R_FindPlane(frontsector, frontsector->ceilingheight, frontsector->ceilingpic,
 			ceilinglightlevel, frontsector->ceilingxoffset, frontsector->ceilingyoffset, frontsector->ceilingangle,
-			ceilingcolormap, NULL, NULL, frontsector->c_slope, P_SectorHasCeilingPortal(frontsector) ? &frontsector->portal_ceiling : NULL);
+			ceilingcolormap, NULL, NULL, frontsector->c_slope, P_SectorGetCeilingPortal(frontsector));
 	}
 	else
 		ceilingplane = NULL;
diff --git a/src/r_defs.h b/src/r_defs.h
index 2e58a1bb86407a4f52d0f8f95d917377c4d1c764..a8addfe0b70d10912c00bdf7a2f2aa63c0941b7b 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -210,12 +210,11 @@ typedef enum
 
 typedef enum
 {
-	SECPORTAL_NONE,
 	SECPORTAL_LINE,
 	SECPORTAL_OBJECT,
 	SECPORTAL_SKYBOX,
 	SECPORTAL_FLOOR,
-	SECPORTAL_CEILING,
+	SECPORTAL_CEILING
 } secportaltype_e;
 
 typedef struct sectorportal_s
@@ -519,8 +518,8 @@ typedef struct sector_s
 	extracolormap_t *spawn_extra_colormap;
 
 	// portals
-	sectorportal_t portal_floor;
-	sectorportal_t portal_ceiling;
+	UINT32 portal_floor;
+	UINT32 portal_ceiling;
 } sector_t;
 
 //
diff --git a/src/r_portal.c b/src/r_portal.c
index b6a430b67c199b3635596ef551ba281b9dd314d7..c485aa03380ebe44e5ac895bbdfa087d508b6a7b 100644
--- a/src/r_portal.c
+++ b/src/r_portal.c
@@ -315,8 +315,6 @@ void Portal_AddSkybox (const visplane_t* plane)
 }
 
 /** Creates a sector portal out of a visplane.
- *
- * Mostly the same as Portal_AddSkybox.
  */
 void Portal_AddSectorPortal (const visplane_t* plane)
 {
@@ -324,9 +322,8 @@ void Portal_AddSectorPortal (const visplane_t* plane)
 	fixed_t x, y, z, angle;
 	sectorportal_t *secportal = plane->portalsector;
 
-	if (secportal->type == SECPORTAL_NONE)
-		return;
-	else if (secportal->type == SECPORTAL_SKYBOX)
+	// Shortcut
+	if (secportal->type == SECPORTAL_SKYBOX)
 	{
 		Portal_AddSkybox(plane);
 		return;
diff --git a/src/r_segs.c b/src/r_segs.c
index 81fca64e13ade94fb59fd94d8b59055c0ac4483e..2a86cc41a83765288d6c8ef786fb70592765923f 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1893,7 +1893,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			|| backsector->floorlightsec != frontsector->floorlightsec
 			//SoM: 4/3/2000: Check for colormaps
 			|| frontsector->extra_colormap != backsector->extra_colormap
-			|| !P_CompareSectorPortals(&frontsector->portal_floor, &backsector->portal_floor)
+			|| !P_CompareSectorPortals(P_SectorGetFloorPortal(frontsector), P_SectorGetFloorPortal(backsector))
 			|| (frontsector->ffloors != backsector->ffloors && !Tag_Compare(&frontsector->tags, &backsector->tags)))
 		{
 			markfloor = true;
@@ -1927,7 +1927,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			|| backsector->ceilinglightsec != frontsector->ceilinglightsec
 			//SoM: 4/3/2000: Check for colormaps
 			|| frontsector->extra_colormap != backsector->extra_colormap
-			|| !P_CompareSectorPortals(&frontsector->portal_ceiling, &backsector->portal_ceiling)
+			|| !P_CompareSectorPortals(P_SectorGetCeilingPortal(frontsector), P_SectorGetCeilingPortal(backsector))
 			|| (frontsector->ffloors != backsector->ffloors && !Tag_Compare(&frontsector->tags, &backsector->tags)))
 		{
 			markceiling = true;