diff --git a/src/d_main.c b/src/d_main.c
index fe10f6a38909ed946c3e50957e7c8af661432fc5..d2d796fda6af30cca1f98e33afbc6b92a65bc584 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -298,6 +298,31 @@ gamestate_t wipegamestate = GS_LEVEL;
 INT16 wipetypepre = -1;
 INT16 wipetypepost = -1;
 
+static void D_RenderView(UINT32 viewnum)
+{
+	player_t *player = viewnum == 1 ? &players[secondarydisplayplayer] : &players[displayplayer];
+
+	R_PrepareViewWorld(player);
+
+	if (viewworld == NULL)
+		return;
+
+	R_ApplyLevelInterpolators(viewworld, R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);
+
+	if (player->mo || player->playerstate == PST_DEAD)
+	{
+		R_SetViewNum(viewnum);
+
+#ifdef HWRENDER
+		if (rendermode != render_soft)
+			HWR_RenderPlayerView(viewnum, player);
+		else
+#endif
+		if (rendermode != render_none)
+			R_RenderPlayerView(player);
+	}
+}
+
 static void D_Display(void)
 {
 	boolean forcerefresh = false;
@@ -478,42 +503,17 @@ static void D_Display(void)
 
 			if (!automapactive && !dedicated && cv_renderview.value)
 			{
-				R_ApplyLevelInterpolators(players[displayplayer].world, R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT);
 				PS_START_TIMING(ps_rendercalltime);
-				if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
-				{
-					topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
-					objectsdrawn = 0;
-	#ifdef HWRENDER
-					if (rendermode != render_soft)
-						HWR_RenderPlayerView(0, &players[displayplayer]);
-					else
-	#endif
-					if (rendermode != render_none)
-						R_RenderPlayerView(&players[displayplayer]);
-				}
-
-				// render the second screen
-				if (splitscreen && players[secondarydisplayplayer].mo)
-				{
-	#ifdef HWRENDER
-					if (rendermode != render_soft)
-						HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
-					else
-	#endif
-					if (rendermode != render_none)
-					{
-						viewwindowy = vid.height / 2;
-						M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
+				objectsdrawn = 0;
 
-						topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
+				D_RenderView(0);
 
-						R_RenderPlayerView(&players[secondarydisplayplayer]);
+				// render the second screen
+				if (splitscreen)
+					D_RenderView(1);
 
-						viewwindowy = 0;
-						M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
-					}
-				}
+				for (INT32 wi = 0; wi < numworlds; wi++)
+					worldlist[wi]->interpolated_level_this_frame = false;
 
 				// Image postprocessing effect
 				if (rendermode == render_soft)
diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c
index 4db69ff8b130b00eb46c403f8a47a8c86e535947..fc4158733bb64bb7d2d15c7d061c199f8530b944 100644
--- a/src/hardware/hw_bsp.c
+++ b/src/hardware/hw_bsp.c
@@ -23,17 +23,15 @@
 #include "../i_video.h"
 #include "../w_wad.h"
 #include "../p_setup.h" // levelfadecol
+#include "../p_world.h"
 
 // --------------------------------------------------------------------------
 // This is global data for planes rendering
 // --------------------------------------------------------------------------
 
-extrasubsector_t *extrasubsectors = NULL;
-
 // newsubsectors are subsectors without segs, added for the plane polygons
 #define NEWSUBSECTORS 50
 static size_t totsubsectors;
-size_t addsubsector;
 
 typedef struct
 {
@@ -45,121 +43,24 @@ typedef struct
 //                                    FLOOR & CEILING CONVEX POLYS GENERATION
 // ==========================================================================
 
-//debug counters
-static INT32 nobackpoly = 0;
-static INT32 skipcut = 0;
-static INT32 totalsubsecpolys = 0;
-
-// --------------------------------------------------------------------------
-// Polygon fast alloc / free
-// --------------------------------------------------------------------------
-//hurdler: quick fix for those who wants to play with larger wad
-
-#define ZPLANALLOC
-#ifndef ZPLANALLOC
-//#define POLYPOOLSIZE 1024000 // may be much over what is needed
-/// \todo check out how much is used
-static size_t POLYPOOLSIZE = 1024000;
-
-static UINT8 *gl_polypool = NULL;
-static UINT8 *gl_ppcurrent;
-static size_t gl_ppfree;
-#endif
-
-// only between levels, clear poly pool
-static void HWR_ClearPolys(void)
-{
-#ifndef ZPLANALLOC
-	gl_ppcurrent = gl_polypool;
-	gl_ppfree = POLYPOOLSIZE;
-#endif
-}
-
-// allocate  pool for fast alloc of polys
-void HWR_InitPolyPool(void)
-{
-#ifndef ZPLANALLOC
-	INT32 pnum;
-
-	//hurdler: quick fix for those who wants to play with larger wad
-	if ((pnum = M_CheckParm("-polypoolsize")))
-		POLYPOOLSIZE = atoi(myargv[pnum+1])*1024; // (in kb)
-
-	CONS_Debug(DBG_RENDER, "HWR_InitPolyPool(): allocating %d bytes\n", POLYPOOLSIZE);
-	gl_polypool = malloc(POLYPOOLSIZE);
-	if (!gl_polypool)
-		I_Error("HWR_InitPolyPool(): couldn't malloc polypool\n");
-	HWR_ClearPolys();
-#endif
-}
-
-void HWR_FreePolyPool(void)
-{
-#ifndef ZPLANALLOC
-	if (gl_polypool)
-		free(gl_polypool);
-	gl_polypool = NULL;
-#endif
-}
-
 static poly_t *HWR_AllocPoly(INT32 numpts)
 {
-	poly_t *p;
 	size_t size = sizeof (poly_t) + sizeof (polyvertex_t) * numpts;
-#ifdef ZPLANALLOC
-	p = Z_Malloc(size, PU_HWRPLANE, NULL);
-#else
-#ifdef PARANOIA
-	if (!gl_polypool)
-		I_Error("Used gl_polypool without init!\n");
-	if (!gl_ppcurrent)
-		I_Error("gl_ppcurrent == NULL!\n");
-#endif
-
-	if (gl_ppfree < size)
-		I_Error("HWR_AllocPoly(): no more memory %u bytes left, %u bytes needed\n\n%s\n",
-		        gl_ppfree, size, "You can try the param -polypoolsize 2048 (or higher if needed)");
-
-	p = (poly_t *)gl_ppcurrent;
-	gl_ppcurrent += size;
-	gl_ppfree -= size;
-#endif
+	poly_t *p = Z_Malloc(size, PU_HWRPLANE, NULL);
 	p->numpts = numpts;
 	return p;
 }
 
 static polyvertex_t *HWR_AllocVertex(void)
 {
-	polyvertex_t *p;
-	size_t size = sizeof (polyvertex_t);
-#ifdef ZPLANALLOC
-	p = Z_Malloc(size, PU_HWRPLANE, NULL);
-#else
-	if (gl_ppfree < size)
-		I_Error("HWR_AllocVertex(): no more memory %u bytes left, %u bytes needed\n\n%s\n",
-		        gl_ppfree, size, "You can try the param -polypoolsize 2048 (or higher if needed)");
-
-	p = (polyvertex_t *)gl_ppcurrent;
-	gl_ppcurrent += size;
-	gl_ppfree -= size;
-#endif
-	return p;
+	return Z_Malloc(sizeof (polyvertex_t), PU_HWRPLANE, NULL);
 }
 
-/// \todo polygons should be freed in reverse order for efficiency,
-/// for now don't free because it doesn't free in reverse order
 static void HWR_FreePoly(poly_t *poly)
 {
-#ifdef ZPLANALLOC
 	Z_Free(poly);
-#else
-	const size_t size = sizeof (poly_t) + sizeof (polyvertex_t) * poly->numpts;
-	memset(poly, 0x00, size);
-	//mempoly -= polysize;
-#endif
 }
 
-
 // Return interception along bsp line,
 // with the polygon segment
 //
@@ -532,9 +433,6 @@ static poly_t *CutOutSubsecPoly(seg_t *lseg, INT32 count, poly_t *poly)
 			// only when the cut is not needed it seems (when the cut
 			// line is aligned to one of the borders of the poly, and
 			// only some times..)
-			else
-				skipcut++;
-			//    I_Error("CutOutPoly: only one point for split line (%d %d) %d", ps, pe, debugpos);
 		}
 	}
 	return poly;
@@ -551,16 +449,15 @@ static inline void HWR_SubsecPoly(INT32 num, poly_t *poly)
 	subsector_t *sub;
 	seg_t *lseg;
 
-	sub = &subsectors[num];
+	sub = &world->subsectors[num];
 	count = sub->numlines;
-	lseg = &segs[sub->firstline];
+	lseg = &world->segs[sub->firstline];
 
 	if (poly)
 	{
 		poly = CutOutSubsecPoly (lseg,count,poly);
-		totalsubsecpolys++;
 		//extra data for this subsector
-		extrasubsectors[num].planepoly = poly;
+		world->extrasubsectors[num].planepoly = poly;
 	}
 }
 
@@ -627,13 +524,13 @@ static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *b
 			if (poly && poly->numpts > 2)
 			{
 				CONS_Debug(DBG_RENDER, "Adding a new subsector\n");
-				if (addsubsector == numsubsectors + NEWSUBSECTORS)
+				if (world->numextrasubsectors == numsubsectors + NEWSUBSECTORS)
 					I_Error("WalkBSPNode: not enough addsubsectors\n");
-				else if (addsubsector > 0x7fff)
+				else if (world->numextrasubsectors > 0x7fff)
 					I_Error("WalkBSPNode: addsubsector > 0x7fff\n");
-				*leafnode = (UINT16)((UINT16)addsubsector | NF_SUBSECTOR);
-				extrasubsectors[addsubsector].planepoly = poly;
-				addsubsector++;
+				*leafnode = (UINT16)((UINT16)world->numextrasubsectors | NF_SUBSECTOR);
+				world->extrasubsectors[world->numextrasubsectors].planepoly = poly;
+				world->numextrasubsectors++;
 			}
 
 			//add subsectors without segs here?
@@ -653,7 +550,7 @@ static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *b
 #endif
 		}
 		M_ClearBox(bbox);
-		poly = extrasubsectors[bspnum & ~NF_SUBSECTOR].planepoly;
+		poly = world->extrasubsectors[bspnum & ~NF_SUBSECTOR].planepoly;
 
 		for (i = 0, pt = poly->pts; i < poly->numpts; i++,pt++)
 			M_AddToBox(bbox, FLOAT_TO_FIXED(pt->x), FLOAT_TO_FIXED(pt->y));
@@ -666,10 +563,6 @@ static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *b
 	SplitPoly(&fdivline, poly, &frontpoly, &backpoly);
 	poly = NULL;
 
-	//debug
-	if (!backpoly)
-		nobackpoly++;
-
 	// Recursively divide front space.
 	if (frontpoly)
 	{
@@ -696,11 +589,10 @@ static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *b
 }
 
 // FIXME: use Z_Malloc() STATIC ?
-void HWR_FreeExtraSubsectors(void)
+void HWR_FreeExtraSubsectors(extrasubsector_t *sub)
 {
-	if (extrasubsectors)
-		free(extrasubsectors);
-	extrasubsectors = NULL;
+	if (sub)
+		free(sub);
 }
 
 #define MAXDIST 1.5f
@@ -774,7 +666,7 @@ static void SearchSegInBSP(INT32 bspnum,polyvertex_t *p,poly_t *poly)
 		if (bspnum != -1)
 		{
 			bspnum &= ~NF_SUBSECTOR;
-			q = extrasubsectors[bspnum].planepoly;
+			q = world->extrasubsectors[bspnum].planepoly;
 			if (poly == q || !q)
 				return;
 			for (j = 0; j < q->numpts; j++)
@@ -795,7 +687,7 @@ static void SearchSegInBSP(INT32 bspnum,polyvertex_t *p,poly_t *poly)
 					for (n = k+1; n < newpoly->numpts; n++)
 						newpoly->pts[n] = q->pts[n-1];
 					numsplitpoly++;
-					extrasubsectors[bspnum].planepoly =
+					world->extrasubsectors[bspnum].planepoly =
 						newpoly;
 					HWR_FreePoly(q);
 					return;
@@ -840,9 +732,9 @@ static INT32 SolveTProblem(void)
 
 	numsplitpoly = 0;
 
-	for (l = 0; l < addsubsector; l++)
+	for (l = 0; l < world->numextrasubsectors; l++)
 	{
-		p = extrasubsectors[l].planepoly;
+		p = world->extrasubsectors[l].planepoly;
 		if (p)
 			for (i = 0; i < p->numpts; i++)
 				SearchSegInBSP((INT32)numnodes-1, &p->pts[i], p);
@@ -870,9 +762,9 @@ static void AdjustSegs(void)
 
 	for (i = 0; i < numsubsectors; i++)
 	{
-		count = subsectors[i].numlines;
-		lseg = &segs[subsectors[i].firstline];
-		p = extrasubsectors[i].planepoly;
+		count = world->subsectors[i].numlines;
+		lseg = &world->segs[world->subsectors[i].firstline];
+		p = world->extrasubsectors[i].planepoly;
 		//if (!p)
 			//continue;
 		for (; count--; lseg++)
@@ -955,40 +847,28 @@ void HWR_CreatePlanePolygons(INT32 bspnum)
 {
 	poly_t *rootp;
 	polyvertex_t *rootpv;
-	size_t i;
 	fixed_t rootbbox[4];
 
 	CONS_Debug(DBG_RENDER, "Creating polygons, please wait...\n");
+
 #ifdef HWR_LOADING_SCREEN
 	ls_count = ls_percent = 0; // reset the loading status
 	CON_Drawer(); //let the user know what we are doing
 	I_FinishUpdate(); // page flip or blit buffer
 #endif
 
-	HWR_ClearPolys();
-
 	// find min/max boundaries of map
-	//CONS_Debug(DBG_RENDER, "Looking for boundaries of map...\n");
 	M_ClearBox(rootbbox);
-	for (i = 0;i < numvertexes; i++)
-		M_AddToBox(rootbbox, vertexes[i].x, vertexes[i].y);
-
-	//CONS_Debug(DBG_RENDER, "Generating subsector polygons... %d subsectors\n", numsubsectors);
+	for (size_t i = 0; i < world->numvertexes; i++)
+		M_AddToBox(rootbbox, world->vertexes[i].x, world->vertexes[i].y);
 
-	HWR_FreeExtraSubsectors();
 	// allocate extra data for each subsector present in map
-	totsubsectors = numsubsectors + NEWSUBSECTORS;
-	extrasubsectors = calloc(totsubsectors, sizeof (*extrasubsectors));
-	if (extrasubsectors == NULL)
+	totsubsectors = world->numsubsectors + NEWSUBSECTORS;
+	world->extrasubsectors = calloc(totsubsectors, sizeof (*world->extrasubsectors));
+	if (world->extrasubsectors == NULL)
 		I_Error("couldn't malloc extrasubsectors totsubsectors %s\n", sizeu1(totsubsectors));
 
-	// allocate table for back to front drawing of subsectors
-	/*gl_drawsubsectors = (INT16 *)malloc(sizeof (*gl_drawsubsectors) * totsubsectors);
-	if (!gl_drawsubsectors)
-		I_Error("couldn't malloc gl_drawsubsectors\n");*/
-
-	// number of the first new subsector that might be added
-	addsubsector = numsubsectors;
+	world->numextrasubsectors = world->numsubsectors; // number of the first new subsector that might be added
 
 	// construct the initial convex poly that encloses the full map
 	rootp = HWR_AllocPoly(4);
@@ -1007,20 +887,10 @@ void HWR_CreatePlanePolygons(INT32 bspnum)
 	rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]);  //ll
 	rootpv++;
 
-	WalkBSPNode(bspnum, rootp, NULL,rootbbox);
+	WalkBSPNode(bspnum, rootp, NULL, rootbbox);
 
-	i = SolveTProblem();
-	//CONS_Debug(DBG_RENDER, "%d point divides a polygon line\n",i);
+	SolveTProblem();
 	AdjustSegs();
-
-	//debug debug..
-	//if (nobackpoly)
-	//    CONS_Debug(DBG_RENDER, "no back polygon %u times\n",nobackpoly);
-	//"(should happen only with the deep water trick)"
-	//if (skipcut)
-	//    CONS_Debug(DBG_RENDER, "%u cuts were skipped because of only one point\n",skipcut);
-
-	//CONS_Debug(DBG_RENDER, "done: %u total subsector convex polygons\n", totalsubsecpolys);
 }
 
 #endif //HWRENDER
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index b5daba82261ce46cbe0393ca765146b4118fcfff..3c30d3c8ce4c00fc2e1984a0f0a1030824f796ab 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -28,35 +28,6 @@
 // structures
 // -----------
 
-// a vertex of a Doom 'plane' polygon
-typedef struct
-{
-	float x;
-	float y;
-	float z;
-} polyvertex_t;
-
-#ifdef _MSC_VER
-#pragma warning(disable :  4200)
-#endif
-
-// a convex 'plane' polygon, clockwise order
-typedef struct
-{
-	INT32 numpts;
-	polyvertex_t pts[0];
-} poly_t;
-
-#ifdef _MSC_VER
-#pragma warning(default :  4200)
-#endif
-
-// holds extra info for 3D render, for each subsector in subsectors[]
-typedef struct
-{
-	poly_t *planepoly;  // the generated convex polygon
-} extrasubsector_t;
-
 // needed for sprite rendering
 // equivalent of the software renderer's vissprites
 typedef struct gl_vissprite_s
@@ -97,13 +68,7 @@ typedef struct gl_vissprite_s
 // --------
 // hw_bsp.c
 // --------
-extern extrasubsector_t *extrasubsectors;
-extern size_t addsubsector;
-
-void HWR_InitPolyPool(void);
-void HWR_FreePolyPool(void);
-
-void HWR_FreeExtraSubsectors(void);
+void HWR_FreeExtraSubsectors(extrasubsector_t *sub);
 
 // --------
 // hw_cache.c
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index add1074ee23997a47dfd0fea51eb549db5e6d263..6d80cac4332e2faf92345e8103b7a49333c6a6ef 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -50,7 +50,6 @@
 
 #define R_FAKEFLOORS
 #define HWPRECIP
-//#define POLYSKY
 
 // ==========================================================================
 // the hardware driver object
@@ -102,10 +101,6 @@ static angle_t gl_xtoviewangle[MAXVIDWIDTH+1];
 #define DOPLANES
 //#define DOWALLS
 
-// test of drawing sky by polygons like in software with visplane, unfortunately
-// this doesn't work since we must have z for pixel and z for texture (not like now with z = oow)
-//#define POLYSKY
-
 // test change fov when looking up/down but bsp projection messup :(
 //#define NOCRAPPYMLOOK
 
@@ -639,51 +634,6 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 	HWR_PlaneLighting(planeVerts, nrPlaneVerts);
 #endif
 }
-
-#ifdef POLYSKY
-// this don't draw anything it only update the z-buffer so there isn't problem with
-// wall/things upper that sky (map12)
-static void HWR_RenderSkyPlane(extrasubsector_t *xsub, fixed_t fixedheight)
-{
-	polyvertex_t *pv;
-	float height; //constant y for all points on the convex flat polygon
-	FOutVector *v3d;
-	INT32 nrPlaneVerts;   //verts original define of convex flat polygon
-	INT32 i;
-
-	// no convex poly were generated for this subsector
-	if (!xsub->planepoly)
-		return;
-
-	height = FIXED_TO_FLOAT(fixedheight);
-
-	pv  = xsub->planepoly->pts;
-	nrPlaneVerts = xsub->planepoly->numpts;
-
-	if (nrPlaneVerts < 3) // not even a triangle?
-		return;
-
-	if (nrPlaneVerts > MAXPLANEVERTICES) // FIXME: exceeds plVerts size
-	{
-		CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, MAXPLANEVERTICES);
-		return;
-	}
-
-	// transform
-	v3d = planeVerts;
-	for (i = 0; i < nrPlaneVerts; i++,v3d++,pv++)
-	{
-		v3d->s = 0.0f;
-		v3d->t = 0.0f;
-		v3d->x = pv->x;
-		v3d->y = height;
-		v3d->z = pv->y;
-	}
-
-	HWD.pfnDrawPolygon(NULL, planeVerts, nrPlaneVerts, PF_Invisible|PF_NoTexture|PF_Occlude);
-}
-#endif //polysky
-
 #endif //doplanes
 
 FBITFIELD HWR_GetBlendModeFlag(INT32 style)
@@ -2903,14 +2853,9 @@ static void HWR_Subsector(size_t num)
 	ffloor_t *rover;
 
 #ifdef PARANOIA //no risk while developing, enough debugging nights!
-	if (num >= addsubsector)
+	if (num >= world->numextrasubsectors)
 		I_Error("HWR_Subsector: ss %s with numss = %s, addss = %s\n",
-			sizeu1(num), sizeu2(numsubsectors), sizeu3(addsubsector));
-
-	/*if (num >= numsubsectors)
-		I_Error("HWR_Subsector: ss %i with numss = %i",
-		        num,
-		        numsubsectors);*/
+			sizeu1(num), sizeu2(numsubsectors), sizeu3(world->numextrasubsectors));
 #endif
 
 	if (num < numsubsectors)
@@ -3005,19 +2950,13 @@ static void HWR_Subsector(size_t num)
 			if (sub->validcount != validcount)
 			{
 				HWR_GetLevelFlat(&viewworld->flats[gl_frontsector->floorpic]);
-				HWR_RenderPlane(sub, &extrasubsectors[num], false,
+				HWR_RenderPlane(sub, &world->extrasubsectors[num], false,
 					// Hack to make things continue to work around slopes.
 					locFloorHeight == cullFloorHeight ? locFloorHeight : gl_frontsector->floorheight,
 					// We now return you to your regularly scheduled rendering.
 					PF_Occlude, floorlightlevel, &viewworld->flats[gl_frontsector->floorpic], NULL, 255, floorcolormap);
 			}
 		}
-		else
-		{
-#ifdef POLYSKY
-			HWR_RenderSkyPlane(&extrasubsectors[num], locFloorHeight);
-#endif
-		}
 	}
 
 	if (cullCeilingHeight > dup_viewz)
@@ -3027,26 +2966,18 @@ static void HWR_Subsector(size_t num)
 			if (sub->validcount != validcount)
 			{
 				HWR_GetLevelFlat(&viewworld->flats[gl_frontsector->ceilingpic]);
-				HWR_RenderPlane(sub, &extrasubsectors[num], true,
+				HWR_RenderPlane(sub, &world->extrasubsectors[num], true,
 					// Hack to make things continue to work around slopes.
 					locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gl_frontsector->ceilingheight,
 					// We now return you to your regularly scheduled rendering.
 					PF_Occlude, ceilinglightlevel, &viewworld->flats[gl_frontsector->ceilingpic], NULL, 255, ceilingcolormap);
 			}
 		}
-		else
-		{
-#ifdef POLYSKY
-			HWR_RenderSkyPlane(&extrasubsectors[num], locCeilingHeight);
-#endif
-		}
 	}
 
-#ifndef POLYSKY
 	// Moved here because before, when above the ceiling and the floor does not have the sky flat, it doesn't draw the sky
 	if (gl_frontsector->ceilingpic == viewworld->skyflatnum || gl_frontsector->floorpic == viewworld->skyflatnum)
 		drawsky = true;
-#endif
 
 #ifdef R_FAKEFLOORS
 	if (gl_frontsector->ffloors)
@@ -3078,9 +3009,9 @@ static void HWR_Subsector(size_t num)
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 					alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap);
 
-					HWR_AddTransparentFloor(0,
-					                       &extrasubsectors[num],
-										   false,
+					HWR_AddTransparentFloor(NULL,
+					                       &world->extrasubsectors[num],
+					                       false,
 					                       *rover->bottomheight,
 					                       *gl_frontsector->lightlist[light].lightlevel,
 					                       alpha, rover->master->frontsector, PF_Fog|PF_NoTexture,
@@ -3090,9 +3021,9 @@ static void HWR_Subsector(size_t num)
 				{
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 
-					HWR_AddTransparentFloor(&world->flats[*rover->bottompic],
-					                       &extrasubsectors[num],
-										   false,
+					HWR_AddTransparentFloor(&viewworld->flats[*rover->bottompic],
+					                       &world->extrasubsectors[num],
+					                       false,
 					                       *rover->bottomheight,
 					                       *gl_frontsector->lightlist[light].lightlevel,
 					                       rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector,
@@ -3101,9 +3032,9 @@ static void HWR_Subsector(size_t num)
 				}
 				else
 				{
-					HWR_GetLevelFlat(&world->flats[*rover->bottompic]);
+					HWR_GetLevelFlat(&viewworld->flats[*rover->bottompic]);
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
-					HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &world->flats[*rover->bottompic],
+					HWR_RenderPlane(sub, &world->extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &viewworld->flats[*rover->bottompic],
 					                rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap);
 				}
 			}
@@ -3124,21 +3055,21 @@ static void HWR_Subsector(size_t num)
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 					alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap);
 
-					HWR_AddTransparentFloor(0,
-					                       &extrasubsectors[num],
-										   true,
+					HWR_AddTransparentFloor(NULL,
+					                       &world->extrasubsectors[num],
+					                       true,
 					                       *rover->topheight,
 					                       *gl_frontsector->lightlist[light].lightlevel,
 					                       alpha, rover->master->frontsector, PF_Fog|PF_NoTexture,
-										   true, rover->master->frontsector->extra_colormap);
+					                       true, rover->master->frontsector->extra_colormap);
 				}
 				else if ((rover->fofflags & FOF_TRANSLUCENT && !(rover->fofflags & FOF_SPLAT)) || rover->blend)
 				{
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 
-					HWR_AddTransparentFloor(&world->flats[*rover->toppic],
-					                        &extrasubsectors[num],
-											true,
+					HWR_AddTransparentFloor(&viewworld->flats[*rover->toppic],
+					                        &world->extrasubsectors[num],
+					                        true,
 					                        *rover->topheight,
 					                        *gl_frontsector->lightlist[light].lightlevel,
 					                        rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector,
@@ -3147,9 +3078,9 @@ static void HWR_Subsector(size_t num)
 				}
 				else
 				{
-					HWR_GetLevelFlat(&world->flats[*rover->toppic]);
+					HWR_GetLevelFlat(&viewworld->flats[*rover->toppic]);
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
-					HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &world->flats[*rover->toppic],
+					HWR_RenderPlane(sub, &world->extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &viewworld->flats[*rover->toppic],
 					                  rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap);
 				}
 			}
@@ -3249,33 +3180,6 @@ fixed_t *hwbbox;
 
 static void HWR_RenderBSPNode(INT32 bspnum)
 {
-	/*//GZDoom code
-	if(bspnum == -1)
-	{
-		HWR_Subsector(subsectors);
-		return;
-	}
-	while(!((size_t)bspnum&(~NF_SUBSECTOR))) // Keep going until found a subsector
-	{
-		node_t *bsp = &nodes[bspnum];
-
-		// Decide which side the view point is on
-		INT32 side = R_PointOnSide(dup_viewx, dup_viewy, bsp);
-
-		// Recursively divide front space (toward the viewer)
-		HWR_RenderBSPNode(bsp->children[side]);
-
-		// Possibly divide back space (away from viewer)
-		side ^= 1;
-
-		if (!HWR_CheckBBox(bsp->bbox[side]))
-			return;
-
-		bspnum = bsp->children[side];
-	}
-
-	HWR_Subsector(bspnum-1);
-*/
 	node_t *bsp = &nodes[bspnum];
 
 	// Decide which side the view point is on
@@ -3288,12 +3192,10 @@ static void HWR_RenderBSPNode(INT32 bspnum)
 	{
 		if (bspnum == -1)
 		{
-			//*(gl_drawsubsector_p++) = 0;
 			HWR_Subsector(0);
 		}
 		else
 		{
-			//*(gl_drawsubsector_p++) = bspnum&(~NF_SUBSECTOR);
 			HWR_Subsector(bspnum&(~NF_SUBSECTOR));
 		}
 		return;
@@ -3317,28 +3219,6 @@ static void HWR_RenderBSPNode(INT32 bspnum)
 	}
 }
 
-/*
-//
-// Clear 'stack' of subsectors to draw
-//
-static void HWR_ClearDrawSubsectors(void)
-{
-	gl_drawsubsector_p = gl_drawsubsectors;
-}
-
-//
-// Draw subsectors pushed on the drawsubsectors 'stack', back to front
-//
-static void HWR_RenderSubsectors(void)
-{
-	while (gl_drawsubsector_p > gl_drawsubsectors)
-	{
-		HWR_RenderBSPNode(
-		lastsubsec->nextsubsec = bspnum & (~NF_SUBSECTOR);
-	}
-}
-*/
-
 // ==========================================================================
 //                                                              FROM R_MAIN.C
 // ==========================================================================
@@ -6189,7 +6069,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 	const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd);
 	postimg_t *type;
 
-	const boolean skybox = (world->skyboxmo[0] && cv_skybox.value); // True if there's a skybox object and skyboxes are on
+	const boolean skybox = viewworld->skyboxmo[0] && cv_skybox.value; // True if there's a skybox object and skyboxes are on
 
 	FRGBAFloat ClearColor;
 
@@ -6418,7 +6298,8 @@ void HWR_LoadLevel(void)
 	HWR_ResetLights();
 #endif
 
-	HWR_CreatePlanePolygons((INT32)numnodes - 1);
+	if (world->extrasubsectors == NULL)
+		HWR_CreatePlanePolygons((INT32)world->numnodes - 1);
 
 	// Build the sky dome
 	HWR_ClearSkyDome();
@@ -6537,7 +6418,6 @@ void HWR_Startup(void)
 	{
 		CONS_Printf("HWR_Startup()...\n");
 
-		HWR_InitPolyPool();
 		HWR_AddSessionCommands();
 		HWR_InitMapTextures();
 		HWR_InitModels();
@@ -6587,8 +6467,6 @@ void HWR_Switch(void)
 void HWR_Shutdown(void)
 {
 	CONS_Printf("HWR_Shutdown()\n");
-	HWR_FreeExtraSubsectors();
-	HWR_FreePolyPool();
 	HWR_FreeMapTextures();
 	HWD.pfnFlushScreenTextures();
 }
diff --git a/src/p_setup.c b/src/p_setup.c
index 4995b7b42f8814efe96163bafab68208592a7430..78c8659ee352c5532405957c7923c5a1125072b8 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7853,9 +7853,6 @@ boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boo
 #ifdef HWRENDER // not win32 only 19990829 by Kin
 	gl_maploaded = false;
 
-	// Lactozilla: Free extrasubsectors regardless of renderer.
-	HWR_FreeExtraSubsectors();
-
 	// Create plane polygons.
 	if (rendermode == render_opengl)
 		HWR_LoadLevel();
@@ -7941,7 +7938,7 @@ boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boo
 
 	R_ResetViewInterpolation(0);
 	R_ResetViewInterpolation(0);
-	R_UpdateMobjInterpolators();
+	R_UpdateMobjInterpolators(world);
 
 	// Title card!
 	G_StartTitleCard();
diff --git a/src/p_tick.c b/src/p_tick.c
index ed555e18805aebef2397f6e7fcf798e436fc00ce..705a483a6da92728e563ad011754fc77abb6537c 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -762,7 +762,7 @@ void P_Ticker(boolean run)
 		if (OP_FreezeObjectplace())
 		{
 			P_MapStart();
-			R_UpdateMobjInterpolators();
+			R_UpdateMobjInterpolators(world);
 			OP_ObjectplaceMovement(&players[0]);
 			P_MoveChaseCamera(&players[0], &camera, false);
 			R_UpdateViewInterpolation();
@@ -788,7 +788,7 @@ void P_Ticker(boolean run)
 
 	if (run)
 	{
-		R_UpdateMobjInterpolators();
+		R_UpdateMobjInterpolators(world);
 
 		if (demorecording)
 			G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0);
@@ -962,8 +962,6 @@ void P_Ticker(boolean run)
 	}
 
 	P_MapEnd();
-
-//	Z_CheckMemCleanup();
 }
 
 // Abbreviated ticker for pre-loading, calls thinkers and assorted things
@@ -981,7 +979,7 @@ void P_PreTicker(INT32 frames)
 	{
 		P_MapStart();
 
-		R_UpdateMobjInterpolators();
+		R_UpdateMobjInterpolators(world);
 
 		RunLuaHookForWorld(HOOK(PreThinkFrame));
 
diff --git a/src/p_world.c b/src/p_world.c
index d35fe04b4254acfa50315267078bd020bced0644..8289a58321f7b3d0b98a6d8db8d0cdbcf7c6eeb5 100644
--- a/src/p_world.c
+++ b/src/p_world.c
@@ -253,11 +253,6 @@ void P_SwitchWorld(player_t *player, world_t *w)
 	boolean local = ((INT32)playernum == consoleplayer);
 	boolean resetplayer = (player->powers[pw_carry] != CR_PLAYER);
 
-#if 0
-	if (w == player->world)
-		return;
-#endif
-
 	if (!playeringame[playernum] || !player->mo || P_MobjWasRemoved(player->mo))
 		return;
 
@@ -303,6 +298,11 @@ void P_SwitchWorld(player_t *player, world_t *w)
 	if (resetplayer)
 		P_ResetPlayer(player);
 	P_MapEnd();
+
+#ifdef HWRENDER
+	if (rendermode == render_opengl)
+		HWR_LoadLevel();
+#endif
 }
 
 void Command_Switchworld_f(void)
@@ -367,6 +367,12 @@ void P_UnloadWorld(world_t *w)
 
 	LUA_InvalidateLevel(w);
 	P_UnloadSectorAttachments(w->sectors, w->numsectors);
+
+	if (world->extrasubsectors)
+	{
+		HWR_FreeExtraSubsectors(world->extrasubsectors);
+		world->extrasubsectors = NULL;
+	}
 }
 
 //
diff --git a/src/p_world.h b/src/p_world.h
index 919cf915aaf3ce43ce384d18c96f432334f0b963..219e2757340d21f66a1694e9881ec4db471a922a 100644
--- a/src/p_world.h
+++ b/src/p_world.h
@@ -26,10 +26,9 @@
 #define NUMWAYPOINTSEQUENCES 256
 
 //
-// Lactozilla: A "world" is the environment that players interact with.
-// A "map" is what defines how the world is built (what you edit in a level editor.)
-// And you could say that a "level" both describes metadata about that "map" (level headers), and contains a world.
-// (This is my terminology anyway, "map" and "level" are usually interchangeable.)
+// A "world" is the environment that players interact with
+// A "map" is what defines how the world is built (what you edit in a level editor)
+// And a "level" both describes metadata (level headers), and contains many worlds
 //
 typedef struct
 {
@@ -48,6 +47,9 @@ typedef struct
 	side_t *sides;
 	mapthing_t *mapthings;
 
+	extrasubsector_t *extrasubsectors;
+	size_t numextrasubsectors;
+
 	sector_t *spawnsectors;
 	line_t *spawnlines;
 	side_t *spawnsides;
@@ -126,6 +128,8 @@ typedef struct
 	void **interpolated_mobjs;
 	size_t interpolated_mobjs_len;
 	size_t interpolated_mobjs_capacity;
+
+	boolean interpolated_level_this_frame;
 } world_t;
 
 extern world_t *world;
diff --git a/src/r_defs.h b/src/r_defs.h
index 2d19e065b988b5928e2a822b61a9f9756d215463..e52fe9f07463c7d7a8bf031a0733e35b75e92cf4 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -714,6 +714,35 @@ typedef struct
 	UINT16 children[2];
 } node_t;
 
+// a vertex of a Doom 'plane' polygon
+typedef struct
+{
+	float x;
+	float y;
+	float z;
+} polyvertex_t;
+
+#ifdef _MSC_VER
+#pragma warning(disable :  4200)
+#endif
+
+// a convex 'plane' polygon, clockwise order
+typedef struct
+{
+	INT32 numpts;
+	polyvertex_t pts[0];
+} poly_t;
+
+#ifdef _MSC_VER
+#pragma warning(default :  4200)
+#endif
+
+// holds extra info for 3D render, for each subsector in subsectors[]
+typedef struct
+{
+	poly_t *planepoly;  // the generated convex polygon
+} extrasubsector_t;
+
 #if defined(_MSC_VER)
 #pragma pack(1)
 #endif
diff --git a/src/r_fps.c b/src/r_fps.c
index b5fee18764acfae907a42d39229378f973bc1bc9..c358fb36917c17d972d10c47c92920bfc2bb9368 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -556,6 +556,8 @@ void R_ClearLevelInterpolatorState(thinker_t *thinker)
 void R_ApplyLevelInterpolators(void *wptr, fixed_t frac)
 {
 	world_t *w = (world_t *)wptr;
+	if (w->interpolated_level_this_frame)
+		return;
 
 	size_t i, ii;
 
@@ -608,6 +610,8 @@ void R_ApplyLevelInterpolators(void *wptr, fixed_t frac)
 			break;
 		}
 	}
+
+	w->interpolated_level_this_frame = true;
 }
 
 static void R_RestoreLevelInterpolatorsForWorld(world_t *w)
@@ -753,17 +757,24 @@ void R_InitMobjInterpolators(void)
 	world->interpolated_mobjs_capacity = 0;
 }
 
-void R_UpdateMobjInterpolators(void)
+void R_UpdateMobjInterpolators(void *wptr)
+{
+	world_t *w = (world_t *)wptr;
+
+	for (size_t i = 0; i < w->interpolated_mobjs_len; i++)
+	{
+		mobj_t *mobj = w->interpolated_mobjs[i];
+		if (!P_MobjWasRemoved(mobj))
+			R_ResetMobjInterpolationState(mobj);
+	}
+}
+
+void R_UpdateAllMobjInterpolators(void)
 {
 	for (INT32 wi = 0; wi < numworlds; wi++)
 	{
 		world_t *w = worldlist[wi];
-		for (size_t i = 0; i < w->interpolated_mobjs_len; i++)
-		{
-			mobj_t *mobj = w->interpolated_mobjs[i];
-			if (!P_MobjWasRemoved(mobj))
-				R_ResetMobjInterpolationState(mobj);
-		}
+		R_UpdateMobjInterpolators(w);
 	}
 }
 
diff --git a/src/r_fps.h b/src/r_fps.h
index 1c8af5cb6f94d95e46c9a88e26e15c942e12ea83..0252922c5971678506f534b0b763ed588a8588e3 100644
--- a/src/r_fps.h
+++ b/src/r_fps.h
@@ -155,7 +155,8 @@ void R_InitMobjInterpolators(void);
 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_UpdateMobjInterpolators(void *wptr);
+void R_UpdateAllMobjInterpolators(void);
 void R_ResetMobjInterpolationState(mobj_t *mobj);
 void R_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj);
 
diff --git a/src/r_main.c b/src/r_main.c
index b893b587c81cca7ed04fe266869b1f0b43dbaaba..4205534add4f30d160540e45026006a3cc72d41c 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -1472,19 +1472,30 @@ static void Mask_Post (maskcount_t* m)
 	m->vissprites[1] = visspritecount;
 }
 
-// ================
-// R_RenderView
-// ================
+void R_SetViewNum(UINT32 viewnum)
+{
+	if (rendermode != render_soft)
+		return;
 
-//                     FAB NOTE FOR WIN32 PORT !! I'm not finished already,
-// but I suspect network may have problems with the video buffer being locked
-// for all duration of rendering, and being released only once at the end..
-// I mean, there is a win16lock() or something that lasts all the rendering,
-// so maybe we should release screen lock before each netupdate below..?
+	switch (viewnum)
+	{
+	case 0:
+		viewwindowy = 0;
+		M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
+		break;
+	case 1:
+		viewwindowy = vid.height / 2;
+		M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
+		break;
+	}
 
-void R_RenderPlayerView(player_t *player)
+	topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
+}
+
+void R_PrepareViewWorld(player_t *player)
 {
 	viewworld = NULL;
+
 	R_SetViewMobj(player);
 
 	if (r_viewmobj)
@@ -1494,8 +1505,14 @@ void R_RenderPlayerView(player_t *player)
 	else if (localworld && !splitscreen) // Yes?
 		P_SetViewWorld(localworld);
 	else
-		return;
+		viewworld = worldlist[0];
+}
 
+// ================
+// R_RenderView
+// ================
+void R_RenderPlayerView(player_t *player)
+{
 	INT32			nummasks	= 1;
 	maskcount_t*	masks		= malloc(sizeof(maskcount_t));
 
diff --git a/src/r_main.h b/src/r_main.h
index c40f93448b612a8150dd0c4ebaef2bdf21a2a041..dc51815a68a282c20fe4ccf640b8aacc1ced8c27 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -140,6 +140,9 @@ void R_ExecuteSetViewSize(void);
 void R_SetupFrame(player_t *player);
 void R_SkyboxFrame(player_t *player);
 
+void R_SetViewNum(UINT32 viewnum);
+void R_PrepareViewWorld(player_t *player);
+
 boolean R_ViewpointHasChasecam(player_t *player);
 boolean R_IsViewpointThirdPerson(player_t *player, boolean skybox);