diff --git a/src/f_wipe.c b/src/f_wipe.c
index 4b4ecd7e3848990353d1fa6526d47023fe2c1cb3..69e956216225003bdaaaf94920a28e43d5c5354e 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -172,15 +172,6 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
   */
 static void F_DoWipe(fademask_t *fademask)
 {
-#ifdef HWRENDER
-	/// \todo Mask wipes for OpenGL
-	if(rendermode != render_soft)
-	{
-		HWR_DoScreenWipe();
-		return;
-	}
-#endif
-
 	// Software mask wipe -- optimized; though it might not look like it!
 	// Okay, to save you wondering *how* this is more optimized than the simpler
 	// version that came before it...
@@ -344,6 +335,11 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 			I_Sleep();
 		lastwipetic = nowtime;
 
+#ifdef HWRENDER
+		if (rendermode == render_opengl)
+			HWR_DoWipe(wipetype, wipeframe-1); // send in the wipe type and wipeframe because we need to cache the graphic
+		else
+#endif
 		F_DoWipe(fmask);
 		I_OsPolling();
 		I_UpdateNoBlit();
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index c97e516163715c5783557ff76affb626f16ac8db..4b2ff9f22d5ff3268f7e97815339afbcf77c1a8b 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -452,7 +452,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
 	//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
 	if (format2bpp[grtex->mipmap.grInfo.format]==4)
 	{
-		for (i = 3; i < blocksize; i += 4)
+		for (i = 3; i < blocksize*4; i += 4) // blocksize*4 because blocksize doesn't include the bpp
 		{
 			if (block[i] == 0)
 			{
@@ -1002,4 +1002,108 @@ GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum)
 	return HWR_GetCachedGLPatchPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
 }
 
+// Need to do this because they aren't powers of 2
+static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight,
+	lumpnum_t fademasklumpnum, UINT16 fmwidth, UINT16 fmheight)
+{
+	INT32 i,j;
+	fixed_t posx, posy, stepx, stepy;
+	UINT8 *block = mipmap->grInfo.data; // places the data directly into here, it already has the space allocated from HWR_ResizeBlock
+	UINT8 *flat;
+	UINT8 *dest, *src, texel;
+	RGBA_t col;
+
+	// Place the flats data into flat
+	W_ReadLump(fademasklumpnum, Z_Malloc(W_LumpLength(fademasklumpnum),
+		PU_HWRCACHE, &flat));
+
+	stepy = ((INT32)SHORT(fmheight)<<FRACBITS)/pblockheight;
+	stepx = ((INT32)SHORT(fmwidth)<<FRACBITS)/pblockwidth;
+	posy = 0;
+	for (j = 0; j < pblockheight; j++)
+	{
+		posx = 0;
+		dest = &block[j*blockwidth]; // 1bpp
+		src = &flat[(posy>>FRACBITS)*SHORT(fmwidth)];
+		for (i = 0; i < pblockwidth;i++)
+		{
+			// fademask bpp is always 1, and is used just for alpha
+			texel = src[(posx)>>FRACBITS];
+			col = V_GetColor(texel);
+			*dest = col.s.red; // take the red level of the colour and use it for alpha, as fademasks do
+
+			dest++;
+			posx += stepx;
+		}
+		posy += stepy;
+	}
+
+	Z_Free(flat);
+}
+
+static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum)
+{
+	size_t size;
+	UINT16 fmheight = 0, fmwidth = 0;
+	UINT8 *block; // The fade mask's pixels
+
+	// setup the texture info
+	grMipmap->grInfo.format = GR_TEXFMT_ALPHA_8; // put the correct alpha levels straight in so I don't need to convert it later
+	grMipmap->flags = 0;
+
+	size = W_LumpLength(fademasklumpnum);
+
+	switch (size)
+	{
+		// None of these are powers of 2, so I'll need to do what is done for textures and make them powers of 2 before they can be used
+		case 256000: // 640x400
+			fmwidth = 640;
+			fmheight = 400;
+			break;
+		case 64000: // 320x200
+			fmwidth = 320;
+			fmheight = 200;
+			break;
+		case 16000: // 160x100
+			fmwidth = 160;
+			fmheight = 100;
+			break;
+		case 4000: // 80x50 (minimum)
+			fmwidth = 80;
+			fmheight = 50;
+			break;
+		default: // Bad lump
+			CONS_Alert(CONS_WARNING, "Fade mask lump of incorrect size, ignored\n"); // I should avoid this by checking the lumpnum in HWR_RunWipe
+			break;
+	}
+
+	// Thankfully, this will still work for this scenario
+	HWR_ResizeBlock(fmwidth, fmheight, &grMipmap->grInfo);
+
+	grMipmap->width  = blockwidth;
+	grMipmap->height = blockheight;
+
+	block = MakeBlock(grMipmap);
+
+	HWR_DrawFadeMaskInCache(grMipmap, blockwidth, blockheight, fademasklumpnum, fmwidth, fmheight);
+
+	// I DO need to convert this because it isn't power of 2 and we need the alpha
+}
+
+
+void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
+{
+	GLMipmap_t *grmip;
+
+	grmip = &HWR_GetCachedGLPatch(fademasklumpnum)->mipmap;
+
+	if (!grmip->downloaded && !grmip->grInfo.data)
+		HWR_CacheFadeMask(grmip, fademasklumpnum);
+
+	HWD.pfnSetTexture(grmip);
+
+	// The system-memory data can be purged now.
+	Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+}
+
 #endif //HWRENDER
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index ee84e8b1081d091674b345d3ff8fbe9a550ed0a2..88786bc112ae459eb8826fc14ab551d8f479ce98 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -106,6 +106,7 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum);
 void HWR_SetPalette(RGBA_t *palette);
 GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wad, UINT16 lump);
 GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum);
+void HWR_GetFadeMask(lumpnum_t fademasklumpnum);
 
 // --------
 // hw_draw.c
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index b9977968e5ee0bf4fbfd9acd524b5d159355872e..f87af074185fbff39cca880130188052b47253a3 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -66,6 +66,8 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing);
 #ifdef SORTING
 void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub, fixed_t fixedheight,
                              INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap);
+void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector, fixed_t fixedheight,
+                             INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap);
 #else
 static void HWR_Add3DWater(lumpnum_t lumpnum, extrasubsector_t *xsub, fixed_t fixedheight,
                            INT32 lightlevel, INT32 alpha, sector_t *FOFSector);
@@ -1286,6 +1288,25 @@ static void HWR_SplitFog(sector_t *sector, wallVert3D *wallVerts, FSurfaceInfo*
 	HWR_AddTransparentWall(wallVerts, Surf, 0, PF_Translucent|PF_NoTexture, true, lightnum, colormap);
 }
 
+// HWR_DrawSkyWalls
+// Draw walls into the depth buffer so that anything behind is culled properly
+static void HWR_DrawSkyWall(wallVert3D *wallVerts, FSurfaceInfo *Surf, fixed_t bottom, fixed_t top)
+{
+	HWD.pfnSetTexture(NULL);
+	// no texture
+	wallVerts[3].t = wallVerts[2].t = 0;
+	wallVerts[0].t = wallVerts[1].t = 0;
+	wallVerts[0].s = wallVerts[3].s = 0;
+	wallVerts[2].s = wallVerts[1].s = 0;
+	// set top/bottom coords
+	wallVerts[2].y = wallVerts[3].y = FIXED_TO_FLOAT(top); // No real way to find the correct height of this
+	wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(bottom); // worldlow/bottom because it needs to cover up the lower thok barrier wall
+	HWR_ProjectWall(wallVerts, Surf, PF_Invisible|PF_Clip|PF_NoTexture, 255, NULL);
+	// PF_Invisible so it's not drawn into the colour buffer
+	// PF_NoTexture for no texture
+	// PF_Occlude is set in HWR_ProjectWall to draw into the depth buffer
+}
+
 //
 // HWR_StoreWallRange
 // A portion or all of a wall segment will be drawn, from startfrac to endfrac,
@@ -1393,11 +1414,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 		}
 
 		// check TOP TEXTURE
-		if (worldhigh < worldtop && texturetranslation[gr_sidedef->toptexture]
-#ifdef POLYOBJECTS // polyobjects don't have top textures, silly.
-		&& !gr_curline->polyseg
-#endif
-			)
+		if (worldhigh < worldtop && texturetranslation[gr_sidedef->toptexture])
 		{
 			if (drawtextured)
 			{
@@ -1435,11 +1452,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 		}
 
 		// check BOTTOM TEXTURE
-		if (worldlow > worldbottom && texturetranslation[gr_sidedef->bottomtexture]
-#ifdef POLYOBJECTS // polyobjects don't have bottom textures, silly.
-		&& !gr_curline->polyseg
-#endif
-			) //only if VISIBLE!!!
+		if (worldlow > worldbottom && texturetranslation[gr_sidedef->bottomtexture]) //only if VISIBLE!!!
 		{
 			if (drawtextured)
 			{
@@ -1646,6 +1659,20 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 					blendmode = PF_Masked;
 					break;
 			}
+
+#ifdef POLYOBJECTS
+			if (gr_curline->polyseg && gr_curline->polyseg->translucency > 0)
+			{
+				if (gr_curline->polyseg->translucency >= NUMTRANSMAPS) // wall not drawn
+				{
+					Surf.FlatColor.s.alpha = 0x00; // This shouldn't draw anything regardless of blendmode
+					blendmode = PF_Masked;
+				}
+				else
+					blendmode = HWR_TranstableToAlpha(gr_curline->polyseg->translucency, &Surf);
+			}
+#endif
+
 			if (grTex->mipmap.flags & TF_TRANSPARENT)
 				blendmode = PF_Translucent;
 
@@ -1668,6 +1695,85 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 				Surf.FlatColor.rgba = 0xffffffff;
 			}*/
 		}
+
+		// Isn't this just the most lovely mess
+		if (!gr_curline->polyseg) // Don't do it for polyobjects
+		{
+			if (gr_frontsector->ceilingpic == skyflatnum || gr_backsector->ceilingpic == skyflatnum)
+			{
+				fixed_t depthwallheight;
+
+				if (!gr_sidedef->toptexture || (gr_frontsector->ceilingpic == skyflatnum && gr_backsector->ceilingpic == skyflatnum)) // when both sectors are sky, the top texture isn't drawn
+					depthwallheight = gr_frontsector->ceilingheight < gr_backsector->ceilingheight ? gr_frontsector->ceilingheight : gr_backsector->ceilingheight;
+				else
+					depthwallheight = gr_frontsector->ceilingheight > gr_backsector->ceilingheight ? gr_frontsector->ceilingheight : gr_backsector->ceilingheight;
+
+				if (gr_frontsector->ceilingheight-gr_frontsector->floorheight <= 0) // current sector is a thok barrier
+				{
+					if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is also a thok barrier
+					{
+						if (!gr_sidedef->bottomtexture) // Only extend further down if there's no texture
+							HWR_DrawSkyWall(wallVerts, &Surf, worldbottom < worldlow ? worldbottom : worldlow, INT32_MAX);
+						else
+							HWR_DrawSkyWall(wallVerts, &Surf, worldbottom > worldlow ? worldbottom : worldlow, INT32_MAX);
+					}
+					// behind sector is not a thok barrier
+					else if (gr_backsector->ceilingheight <= gr_frontsector->ceilingheight) // behind sector ceiling is lower or equal to current sector
+						HWR_DrawSkyWall(wallVerts, &Surf, depthwallheight, INT32_MAX);
+						// gr_front/backsector heights need to be used here because of the worldtop being set to worldhigh earlier on
+				}
+				else if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is a thok barrier, current sector is not
+				{
+					if (gr_backsector->ceilingheight >= gr_frontsector->ceilingheight // thok barrier ceiling height is equal to or greater than current sector ceiling height
+						|| gr_backsector->floorheight <= gr_frontsector->floorheight // thok barrier ceiling height is equal to or less than current sector floor height
+						|| gr_backsector->ceilingpic != skyflatnum) // thok barrier is not a sky
+						HWR_DrawSkyWall(wallVerts, &Surf, depthwallheight, INT32_MAX);
+				}
+				else // neither sectors are thok barriers
+				{
+					if ((gr_backsector->ceilingheight < gr_frontsector->ceilingheight && !gr_sidedef->toptexture) // no top texture and sector behind is lower
+						|| gr_backsector->ceilingpic != skyflatnum) // behind sector is not a sky
+						HWR_DrawSkyWall(wallVerts, &Surf, depthwallheight, INT32_MAX);
+				}
+			}
+			// And now for sky floors!
+			if (gr_frontsector->floorpic == skyflatnum || gr_backsector->floorpic == skyflatnum)
+			{
+				fixed_t depthwallheight;
+
+				if (!gr_sidedef->bottomtexture)
+					depthwallheight = worldbottom > worldlow ? worldbottom : worldlow;
+				else
+					depthwallheight = worldbottom < worldlow ? worldbottom : worldlow;
+
+				if (gr_frontsector->ceilingheight-gr_frontsector->floorheight <= 0) // current sector is a thok barrier
+				{
+					if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is also a thok barrier
+					{
+						if (!gr_sidedef->toptexture) // Only extend up if there's no texture
+							HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, worldtop > worldhigh ? worldtop : worldhigh);
+						else
+							HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, worldtop < worldhigh ? worldtop : worldhigh);
+					}
+					// behind sector is not a thok barrier
+					else if (gr_backsector->floorheight >= gr_frontsector->floorheight) // behind sector floor is greater or equal to current sector
+						HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, depthwallheight);
+				}
+				else if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is a thok barrier, current sector is not
+				{
+					if (gr_backsector->floorheight <= gr_frontsector->floorheight // thok barrier floor height is equal to or less than current sector floor height
+						|| gr_backsector->ceilingheight >= gr_frontsector->ceilingheight // thok barrier floor height is equal to or greater than current sector ceiling height
+						|| gr_backsector->floorpic != skyflatnum) // thok barrier is not a sky
+						HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, depthwallheight);
+				}
+				else // neither sectors are thok barriers
+				{
+					if ((gr_backsector->floorheight > gr_frontsector->floorheight && !gr_sidedef->bottomtexture) // no bottom texture and sector behind is higher
+						|| gr_backsector->floorpic != skyflatnum) // behind sector is not a sky
+						HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, depthwallheight);
+				}
+			}
+		}
 	}
 	else
 	{
@@ -1707,6 +1813,14 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
 					HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
 			}
 		}
+
+		if (!gr_curline->polyseg)
+		{
+			if (gr_frontsector->ceilingpic == skyflatnum) // It's a single-sided line with sky for its sector
+				HWR_DrawSkyWall(wallVerts, &Surf, worldtop, INT32_MAX);
+			if (gr_frontsector->floorpic == skyflatnum)
+				HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, worldbottom);
+		}
 	}
 
 
@@ -2506,6 +2620,234 @@ static inline void HWR_AddPolyObjectSegs(void)
 	Z_Free(pv1);
 	Z_Free(gr_fakeline);
 }
+
+#ifdef POLYOBJECTS_PLANES
+static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, fixed_t fixedheight,
+									FBITFIELD blendmode, UINT8 lightlevel, lumpnum_t lumpnum, sector_t *FOFsector,
+									UINT8 alpha, extracolormap_t *planecolormap)
+{
+	float           height; //constant y for all points on the convex flat polygon
+	FOutVector      *v3d;
+	INT32             i;
+	float           flatxref,flatyref;
+	float fflatsize;
+	INT32 flatflag;
+	size_t len;
+	float scrollx = 0.0f, scrolly = 0.0f;
+	angle_t angle = 0;
+	FSurfaceInfo    Surf;
+	fixed_t tempxsow, tempytow;
+	size_t nrPlaneVerts;
+
+	static FOutVector *planeVerts = NULL;
+	static UINT16 numAllocedPlaneVerts = 0;
+
+	nrPlaneVerts = polysector->numVertices;
+
+	height = FIXED_TO_FLOAT(fixedheight);
+
+	if (nrPlaneVerts < 3)   //not even a triangle ?
+		return;
+
+	if (nrPlaneVerts > UINT16_MAX) // FIXME: exceeds plVerts size
+	{
+		CONS_Debug(DBG_RENDER, "polygon size of %d exceeds max value of %d vertices\n", nrPlaneVerts, UINT16_MAX);
+		return;
+	}
+
+	// Allocate plane-vertex buffer if we need to
+	if (!planeVerts || nrPlaneVerts > numAllocedPlaneVerts)
+	{
+		numAllocedPlaneVerts = (UINT16)nrPlaneVerts;
+		Z_Free(planeVerts);
+		Z_Malloc(numAllocedPlaneVerts * sizeof (FOutVector), PU_LEVEL, &planeVerts);
+	}
+
+	len = W_LumpLength(lumpnum);
+
+	switch (len)
+	{
+		case 4194304: // 2048x2048 lump
+			fflatsize = 2048.0f;
+			flatflag = 2047;
+			break;
+		case 1048576: // 1024x1024 lump
+			fflatsize = 1024.0f;
+			flatflag = 1023;
+			break;
+		case 262144:// 512x512 lump
+			fflatsize = 512.0f;
+			flatflag = 511;
+			break;
+		case 65536: // 256x256 lump
+			fflatsize = 256.0f;
+			flatflag = 255;
+			break;
+		case 16384: // 128x128 lump
+			fflatsize = 128.0f;
+			flatflag = 127;
+			break;
+		case 1024: // 32x32 lump
+			fflatsize = 32.0f;
+			flatflag = 31;
+			break;
+		default: // 64x64 lump
+			fflatsize = 64.0f;
+			flatflag = 63;
+			break;
+	}
+
+	// reference point for flat texture coord for each vertex around the polygon
+	flatxref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].x) & (~flatflag)) / fflatsize);
+	flatyref = (float)(((fixed_t)FIXED_TO_FLOAT(polysector->origVerts[0].y) & (~flatflag)) / fflatsize);
+
+	// transform
+	v3d = planeVerts;
+
+	if (FOFsector != NULL)
+	{
+		if (fixedheight == FOFsector->floorheight) // it's a floor
+		{
+			scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatsize;
+			scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatsize;
+			angle = FOFsector->floorpic_angle>>ANGLETOFINESHIFT;
+		}
+		else // it's a ceiling
+		{
+			scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatsize;
+			scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatsize;
+			angle = FOFsector->ceilingpic_angle>>ANGLETOFINESHIFT;
+		}
+	}
+	else if (gr_frontsector)
+	{
+		if (fixedheight < dup_viewz) // it's a floor
+		{
+			scrollx = FIXED_TO_FLOAT(gr_frontsector->floor_xoffs)/fflatsize;
+			scrolly = FIXED_TO_FLOAT(gr_frontsector->floor_yoffs)/fflatsize;
+			angle = gr_frontsector->floorpic_angle>>ANGLETOFINESHIFT;
+		}
+		else // it's a ceiling
+		{
+			scrollx = FIXED_TO_FLOAT(gr_frontsector->ceiling_xoffs)/fflatsize;
+			scrolly = FIXED_TO_FLOAT(gr_frontsector->ceiling_yoffs)/fflatsize;
+			angle = gr_frontsector->ceilingpic_angle>>ANGLETOFINESHIFT;
+		}
+	}
+
+	if (angle) // Only needs to be done if there's an altered angle
+	{
+		// This needs to be done so that it scrolls in a different direction after rotation like software
+		tempxsow = FLOAT_TO_FIXED(scrollx);
+		tempytow = FLOAT_TO_FIXED(scrolly);
+		scrollx = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle))));
+		scrolly = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle))));
+
+		// This needs to be done so everything aligns after rotation
+		// It would be done so that rotation is done, THEN the translation, but I couldn't get it to rotate AND scroll like software does
+		tempxsow = FLOAT_TO_FIXED(flatxref);
+		tempytow = FLOAT_TO_FIXED(flatyref);
+		flatxref = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle))));
+		flatyref = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle))));
+	}
+
+	for (i = 0; i < nrPlaneVerts; i++,v3d++)
+	{
+		// Hurdler: add scrolling texture on floor/ceiling
+		v3d->sow = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatsize) - flatxref + scrollx); // Go from the polysector's original vertex locations
+		v3d->tow = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatsize) + scrolly); // Means the flat is offset based on the original vertex locations
+
+		// Need to rotate before translate
+		if (angle) // Only needs to be done if there's an altered angle
+		{
+			tempxsow = FLOAT_TO_FIXED(v3d->sow);
+			tempytow = FLOAT_TO_FIXED(v3d->tow);
+			v3d->sow = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle))));
+			v3d->tow = (FIXED_TO_FLOAT(-FixedMul(tempxsow, FINESINE(angle)) - FixedMul(tempytow, FINECOSINE(angle))));
+		}
+
+		v3d->x = FIXED_TO_FLOAT(polysector->lines[i]->v1->x);
+		v3d->y = height;
+		v3d->z = FIXED_TO_FLOAT(polysector->lines[i]->v1->y);
+	}
+
+
+	if (planecolormap)
+		Surf.FlatColor.rgba = HWR_Lighting(lightlevel, planecolormap->rgba, planecolormap->fadergba, false, true);
+	else
+		Surf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, true);
+
+	if (blendmode & PF_Translucent)
+	{
+		Surf.FlatColor.s.alpha = (UINT8)alpha;
+		blendmode |= PF_Modulated|PF_Occlude|PF_Clip;
+	}
+	else
+		blendmode |= PF_Masked|PF_Modulated|PF_Clip;
+
+	HWD.pfnDrawPolygon(&Surf, planeVerts, nrPlaneVerts, blendmode);
+}
+
+static void HWR_AddPolyObjectPlanes(void)
+{
+	size_t i;
+	sector_t *polyobjsector; 
+
+	// Polyobject Planes need their own function for drawing because they don't have extrasubsectors by themselves
+	// It should be okay because polyobjects should always be convex anyway
+
+	for (i  = 0; i < numpolys; i++)
+	{
+		polyobjsector = po_ptrs[i]->lines[0]->backsector; // the in-level polyobject sector
+
+		if (!(po_ptrs[i]->flags & POF_RENDERPLANES)) // Only render planes when you should
+			continue;
+
+		if (po_ptrs[i]->translucency >= NUMTRANSMAPS)
+			continue;
+
+		if (polyobjsector->floorheight <= gr_frontsector->ceilingheight
+			&& polyobjsector->floorheight >= gr_frontsector->floorheight
+			&& (viewz < polyobjsector->floorheight))
+		{
+			if (po_ptrs[i]->translucency > 0)
+			{
+				FSurfaceInfo Surf;
+				FBITFIELD blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf);
+				HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, po_ptrs[i], polyobjsector->floorheight,
+													polyobjsector->lightlevel, Surf.FlatColor.s.alpha, polyobjsector, blendmode, NULL);
+			}
+			else
+			{
+				HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum);
+				HWR_RenderPolyObjectPlane(po_ptrs[i], polyobjsector->floorheight, PF_Occlude,
+										polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum,
+										polyobjsector, 255, NULL);
+			}
+		}
+
+		if (polyobjsector->ceilingheight >= gr_frontsector->floorheight
+			&& polyobjsector->ceilingheight <= gr_frontsector->ceilingheight
+			&& (viewz > polyobjsector->ceilingheight))
+		{
+			if (po_ptrs[i]->translucency > 0)
+			{
+				FSurfaceInfo Surf;
+				FBITFIELD blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf);
+				HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, po_ptrs[i], polyobjsector->ceilingheight,
+													polyobjsector->lightlevel, Surf.FlatColor.s.alpha, polyobjsector, blendmode, NULL);
+			}
+			else
+			{
+				HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum);
+				HWR_RenderPolyObjectPlane(po_ptrs[i], polyobjsector->ceilingheight, PF_Occlude,
+										polyobjsector->lightlevel, levelflats[polyobjsector->floorpic].lumpnum,
+										polyobjsector, 255, NULL);
+			}
+		}
+	}
+}
+#endif
 #endif
 
 // -----------------+
@@ -2816,8 +3158,13 @@ static void HWR_Subsector(size_t num)
 		// Draw polyobject lines.
 		HWR_AddPolyObjectSegs();
 
-		// Draw polyobject planes
-		//HWR_AddPolyObjectPlanes();
+#ifdef POLYOBJECTS_PLANES
+		if (sub->validcount != validcount) // This validcount situation seems to let us know that the floors have already been drawn.
+		{
+			// Draw polyobject planes
+			HWR_AddPolyObjectPlanes();
+		}
+#endif
 	}
 #endif
 
@@ -3698,6 +4045,22 @@ typedef struct
 static size_t numplanes = 0; // a list of transparent floors to be drawn
 static planeinfo_t *planeinfo = NULL;
 
+typedef struct
+{
+	polyobj_t *polysector;
+	fixed_t fixedheight;
+	INT32 lightlevel;
+	lumpnum_t lumpnum;
+	INT32 alpha;
+	sector_t *FOFSector;
+	FBITFIELD blend;
+	extracolormap_t *planecolormap;
+	INT32 drawcount;
+} polyplaneinfo_t;
+
+static size_t numpolyplanes = 0; // a list of transparent poyobject floors to be drawn
+static polyplaneinfo_t *polyplaneinfo = NULL;
+
 #ifndef SORTING
 size_t numfloors = 0;
 #else
@@ -3707,6 +4070,7 @@ size_t numfloors = 0;
 typedef struct gr_drawnode_s
 {
 	planeinfo_t *plane;
+	polyplaneinfo_t *polyplane;
 	wallinfo_t *wall;
 	gr_vissprite_t *sprite;
 
@@ -3747,6 +4111,35 @@ void HWR_AddTransparentFloor(lumpnum_t lumpnum, extrasubsector_t *xsub,
 	numplanes++;
 }
 
+// Adding this for now until I can create extrasubsector info for polyobjects
+// When that happens it'll just be done through HWR_AddTransparentFloor and HWR_RenderPlane
+void HWR_AddTransparentPolyobjectFloor(lumpnum_t lumpnum, polyobj_t *polysector,
+	fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap)
+{
+	static size_t allocedpolyplanes = 0;
+
+	// Force realloc if buffer has been freed
+	if (!polyplaneinfo)
+		allocedpolyplanes = 0;
+
+	if (allocedpolyplanes < numpolyplanes + 1)
+	{
+		allocedpolyplanes += MAX_TRANSPARENTFLOOR;
+		Z_Realloc(polyplaneinfo, allocedpolyplanes * sizeof (*polyplaneinfo), PU_LEVEL, &polyplaneinfo);
+	}
+
+	polyplaneinfo[numpolyplanes].fixedheight = fixedheight;
+	polyplaneinfo[numpolyplanes].lightlevel = lightlevel;
+	polyplaneinfo[numpolyplanes].lumpnum = lumpnum;
+	polyplaneinfo[numpolyplanes].polysector = polysector;
+	polyplaneinfo[numpolyplanes].alpha = alpha;
+	polyplaneinfo[numpolyplanes].FOFSector = FOFSector;
+	polyplaneinfo[numpolyplanes].blend = blend;
+	polyplaneinfo[numpolyplanes].planecolormap = planecolormap;
+	polyplaneinfo[numpolyplanes].drawcount = drawcount++;
+	numpolyplanes++;
+}
+
 //
 // HWR_CreateDrawNodes
 // Creates and sorts a list of drawnodes for the scene being rendered.
@@ -3759,12 +4152,13 @@ static void HWR_CreateDrawNodes(void)
 	// Could this be optimized into _AddTransparentWall/_AddTransparentPlane?
 	// Hell yes! But sort algorithm must be modified to use a linked list.
 	gr_drawnode_t *sortnode = Z_Calloc((sizeof(planeinfo_t)*numplanes)
+									+ (sizeof(polyplaneinfo_t)*numpolyplanes)
 									+ (sizeof(wallinfo_t)*numwalls)
 									,PU_STATIC, NULL);
 	// todo:
 	// However, in reality we shouldn't be re-copying and shifting all this information
 	// that is already lying around. This should all be in some sort of linked list or lists.
-	size_t *sortindex = Z_Calloc(sizeof(size_t) * (numplanes + numwalls), PU_STATIC, NULL);
+	size_t *sortindex = Z_Calloc(sizeof(size_t) * (numplanes + numpolyplanes + numwalls), PU_STATIC, NULL);
 
 	// If true, swap the draw order.
 	boolean shift = false;
@@ -3775,6 +4169,12 @@ static void HWR_CreateDrawNodes(void)
 		sortindex[p] = p;
 	}
 
+	for (i = 0; i < numpolyplanes; i++, p++)
+	{
+		sortnode[p].polyplane = &polyplaneinfo[i];
+		sortindex[p] = p;
+	}
+
 	for (i = 0; i < numwalls; i++, p++)
 	{
 		sortnode[p].wall = &wallinfo[i];
@@ -3810,6 +4210,12 @@ static void HWR_CreateDrawNodes(void)
 					if (ABS(sortnode[sortindex[i]].plane->fixedheight - pviewz) > ABS(sortnode[sortindex[prev]].plane->fixedheight - pviewz))
 						shift = true;
 				}
+				if (sortnode[sortindex[prev]].polyplane)
+				{
+					// Plane (i) is further away than polyplane (prev)
+					if (ABS(sortnode[sortindex[i]].plane->fixedheight - pviewz) > ABS(sortnode[sortindex[prev]].polyplane->fixedheight - pviewz))
+						shift = true;
+				}
 				else if (sortnode[sortindex[prev]].wall)
 				{
 					// Plane (i) is further than wall (prev)
@@ -3817,6 +4223,28 @@ static void HWR_CreateDrawNodes(void)
 						shift = true;
 				}
 			}
+			else if (sortnode[sortindex[i]].polyplane)
+			{
+				// What are we comparing it with?
+				if (sortnode[sortindex[prev]].plane)
+				{
+					// Plane (i) is further away than plane (prev)
+					if (ABS(sortnode[sortindex[i]].polyplane->fixedheight - pviewz) > ABS(sortnode[sortindex[prev]].plane->fixedheight - pviewz))
+						shift = true;
+				}
+				if (sortnode[sortindex[prev]].polyplane)
+				{
+					// Plane (i) is further away than polyplane (prev)
+					if (ABS(sortnode[sortindex[i]].polyplane->fixedheight - pviewz) > ABS(sortnode[sortindex[prev]].polyplane->fixedheight - pviewz))
+						shift = true;
+				}
+				else if (sortnode[sortindex[prev]].wall)
+				{
+					// Plane (i) is further than wall (prev)
+					if (sortnode[sortindex[i]].polyplane->drawcount > sortnode[sortindex[prev]].wall->drawcount)
+						shift = true;
+				}
+			}
 			else if (sortnode[sortindex[i]].wall)
 			{
 				// What are we comparing it with?
@@ -3826,6 +4254,12 @@ static void HWR_CreateDrawNodes(void)
 					if (sortnode[sortindex[i]].wall->drawcount > sortnode[sortindex[prev]].plane->drawcount)
 						shift = true;
 				}
+				if (sortnode[sortindex[prev]].polyplane)
+				{
+					// Wall (i) is further than polyplane(prev)
+					if (sortnode[sortindex[i]].wall->drawcount > sortnode[sortindex[prev]].polyplane->drawcount)
+						shift = true;
+				}
 				else if (sortnode[sortindex[prev]].wall)
 				{
 					// Wall (i) is further than wall (prev)
@@ -3862,6 +4296,16 @@ static void HWR_CreateDrawNodes(void)
 			HWR_RenderPlane(NULL, sortnode[sortindex[i]].plane->xsub, sortnode[sortindex[i]].plane->fixedheight, sortnode[sortindex[i]].plane->blend, sortnode[sortindex[i]].plane->lightlevel,
 				sortnode[sortindex[i]].plane->lumpnum, sortnode[sortindex[i]].plane->FOFSector, sortnode[sortindex[i]].plane->alpha, sortnode[sortindex[i]].plane->fogplane, sortnode[sortindex[i]].plane->planecolormap);
 		}
+		else if (sortnode[sortindex[i]].polyplane)
+		{
+			// We aren't traversing the BSP tree, so make gr_frontsector null to avoid crashes.
+			gr_frontsector = NULL;
+
+			if (!(sortnode[sortindex[i]].polyplane->blend & PF_NoTexture))
+				HWR_GetFlat(sortnode[sortindex[i]].polyplane->lumpnum);
+			HWR_RenderPolyObjectPlane(sortnode[sortindex[i]].polyplane->polysector, sortnode[sortindex[i]].polyplane->fixedheight, sortnode[sortindex[i]].polyplane->blend, sortnode[sortindex[i]].polyplane->lightlevel,
+				sortnode[sortindex[i]].polyplane->lumpnum, sortnode[sortindex[i]].polyplane->FOFSector, sortnode[sortindex[i]].polyplane->alpha, sortnode[sortindex[i]].polyplane->planecolormap);
+		}
 		else if (sortnode[sortindex[i]].wall)
 		{
 			if (!(sortnode[sortindex[i]].wall->blend & PF_NoTexture))
@@ -3873,6 +4317,7 @@ static void HWR_CreateDrawNodes(void)
 
 	numwalls = 0;
 	numplanes = 0;
+	numpolyplanes = 0;
 
 	// No mem leaks, please.
 	Z_Free(sortnode);
@@ -4349,7 +4794,7 @@ static void HWR_DrawSkyBackground(player_t *player)
 {
 	FOutVector v[4];
 	angle_t angle;
-	float f;
+	float dimensionmultiply;
 
 //  3--2
 //  | /|
@@ -4371,36 +4816,49 @@ static void HWR_DrawSkyBackground(player_t *player)
 
 	// X
 
-	if (textures[skytexture]->width > 256)
-		angle = (angle_t)((float)(dup_viewangle + gr_xtoviewangle[0])
-						/((float)textures[skytexture]->width/256.0f))
-							%(ANGLE_90-1);
-	else
-		angle = (dup_viewangle + gr_xtoviewangle[0])%(ANGLE_90-1);
+	// NOTE: This doesn't work right with texture widths greater than 1024
+	// software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly
+	// The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture
 
-	f = (float)((textures[skytexture]->width/2)
-	            * FIXED_TO_FLOAT(FINETANGENT((2048
-	 - ((INT32)angle>>(ANGLETOFINESHIFT + 1))) & FINEMASK)));
+	angle = (dup_viewangle + gr_xtoviewangle[0]);
 
-	v[0].sow = v[3].sow = 0.22f+(f)/(textures[skytexture]->width/2);
-	v[2].sow = v[1].sow = 0.22f+(f+(127))/(textures[skytexture]->width/2);
+	dimensionmultiply = ((float)textures[skytexture]->width/256.0f);
 
+	v[0].sow = v[3].sow = ((float) angle / ((ANGLE_90-1)*dimensionmultiply));
+	v[2].sow = v[1].sow = (-1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply));
 
 	// Y
+	angle = aimingangle;
+	
+	float aspectratio = (float)vid.width/(float)vid.height;
+	dimensionmultiply = ((float)textures[skytexture]->height/(128.0f*aspectratio));
+	float angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply;
 
-	if (textures[skytexture]->height > 256)
-		angle = (angle_t)((float)(aimingangle)
-						*(256.0f/(float)textures[skytexture]->height))
-							%(ANGLE_90-1); // Just so that looking up and down scales right
+	// Middle of the sky should always be at angle 0
+	// need to keep correct aspect ratio with X
+	if (atransform.flip)
+	{
+		// During vertical flip the sky should be flipped and it's y movement should also be flipped obviously
+		v[3].tow = v[2].tow = -(0.5f-(0.5f/dimensionmultiply));
+		v[0].tow = v[1].tow = (-1.0f/dimensionmultiply)-(0.5f-(0.5f/dimensionmultiply));
+	}
 	else
-		angle = (aimingangle);
-
-	f = (float)((textures[skytexture]->height/2)
-	            * FIXED_TO_FLOAT(FINETANGENT((2048
-	 - ((INT32)angle>>(ANGLETOFINESHIFT + 1))) & FINEMASK)));
+	{
+		v[3].tow = v[2].tow = (-1.0f/dimensionmultiply)-(0.5f-(0.5f/dimensionmultiply));
+		v[0].tow = v[1].tow = -(0.5f-(0.5f/dimensionmultiply));
+	}
 
-	v[3].tow = v[2].tow = 0.22f+(f)/(textures[skytexture]->height/2);
-	v[0].tow = v[1].tow = 0.22f+(f+(127))/(textures[skytexture]->height/2);
+	if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa
+	{
+		angle = InvAngle(angle);
+		v[3].tow = v[2].tow += ((float) angle / angleturn);
+		v[0].tow = v[1].tow += ((float) angle / angleturn);
+	}
+	else
+	{
+		v[3].tow = v[2].tow -= ((float) angle / angleturn);
+		v[0].tow = v[1].tow -= ((float) angle / angleturn);
+	}
 
 	HWD.pfnDrawPolygon(NULL, v, 4, 0);
 }
@@ -4649,7 +5107,7 @@ if (0)
 #endif
 
 #ifdef SORTING
-	if (numplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
+	if (numplanes || numpolyplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
 	{
 		HWR_CreateDrawNodes();
 	}
@@ -4879,12 +5337,12 @@ if (0)
 #endif
 
 #ifdef SORTING
-	if (numplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
+	if (numplanes || numpolyplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
 	{
 		HWR_CreateDrawNodes();
 	}
 #else
-	if (numfloors || numwalls)
+	if (numfloors || numpolyplanes || numwalls)
 	{
 		HWD.pfnSetTransform(&atransform);
 		if (numfloors)
@@ -5413,7 +5871,7 @@ void HWR_StartScreenWipe(void)
 
 void HWR_EndScreenWipe(void)
 {
-	HWRWipeCounter = 1.0f;
+	HWRWipeCounter = 0.0f;
 	//CONS_Debug(DBG_RENDER, "In HWR_EndScreenWipe()\n");
 	HWD.pfnEndScreenWipe();
 }
@@ -5423,17 +5881,38 @@ void HWR_DrawIntermissionBG(void)
 	HWD.pfnDrawIntermissionBG();
 }
 
-void HWR_DoScreenWipe(void)
+void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum)
 {
-	//CONS_Debug(DBG_RENDER, "In HWR_DoScreenWipe(). Alpha =%f\n", HWRWipeCounter);
+	static char lumpname[9] = "FADEmmss";
+	lumpnum_t lumpnum;
+	size_t lsize;
+
+	if (wipenum > 99 || scrnnum > 99) // not a valid wipe number
+		return; // shouldn't end up here really, the loop should've stopped running beforehand
+
+	// puts the numbers into the lumpname
+	sprintf(&lumpname[4], "%.2hu%.2hu", (UINT16)wipenum, (UINT16)scrnnum);
+	lumpnum = W_CheckNumForName(lumpname);
+
+	if (lumpnum == LUMPERROR) // again, shouldn't be here really
+		return;
+
+	lsize = W_LumpLength(lumpnum);
+
+	if (!(lsize == 256000 || lsize == 64000 || lsize == 16000 || lsize == 4000))
+	{
+		CONS_Alert(CONS_WARNING, "Fade mask lump %s of incorrect size, ignored\n", lumpname);
+		return; // again, shouldn't get here if it is a bad size
+	}
+
+	HWR_GetFadeMask(lumpnum);
 
-	HWD.pfnDoScreenWipe(HWRWipeCounter);
+	HWD.pfnDoScreenWipe(HWRWipeCounter); // Still send in wipecounter since old stuff might not support multitexturing
 
-	// This works for all the cases in vanilla until fade masks get done
-	HWRWipeCounter -= 0.05f; // Go less opaque after
+	HWRWipeCounter += 0.05f; // increase opacity of end screen
 
-	if (HWRWipeCounter < 0)
-		HWRWipeCounter = 0;
+	if (HWRWipeCounter > 1.0f)
+		HWRWipeCounter = 1.0f;
 }
 
 #endif // HWRENDER
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index 8d8b69e60ee5e87aa7dea9f1015e1df947503401..969946442d1e67ca55db6fde5e238ab5411459db 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -63,8 +63,8 @@ INT32 HWR_GetTextureUsed(void);
 void HWR_DoPostProcessor(player_t *player);
 void HWR_StartScreenWipe(void);
 void HWR_EndScreenWipe(void);
-void HWR_DoScreenWipe(void);
 void HWR_DrawIntermissionBG(void);
+void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum);
 
 // This stuff is put here so MD2's can use them
 UINT32 HWR_Lighting(INT32 light, UINT32 color, UINT32 fadecolor, boolean fogblockpoly, boolean plane);
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 0af7455ede06a87e0f55debe180a1f5eca26f723..02f5053515af59e01cd77c3543ab93d21e19eebf 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1230,7 +1230,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		else
 			p.z = FIXED_TO_FLOAT(spr->mobj->z);
 
-		if (spr->mobj->skin)
+		if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
 			sprdef = &((skin_t *)spr->mobj->skin)->spritedef;
 		else
 			sprdef = &sprites[spr->mobj->sprite];
diff --git a/src/hardware/r_opengl/ogl_win.c b/src/hardware/r_opengl/ogl_win.c
index daf37fe2912ea69e003c627d288e96c73de71b8f..bfdc96d6a5322e4e41ca91e6d2f3d785238decf3 100644
--- a/src/hardware/r_opengl/ogl_win.c
+++ b/src/hardware/r_opengl/ogl_win.c
@@ -366,6 +366,10 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode)
 	else
 		maximumAnisotropy = 0;
 
+#ifndef MINI_GL_COMPATIBILITY
+	SetupGLFunc13();
+#endif
+
 
 	screen_depth = (GLbyte)(lvid->bpp*8);
 	if (screen_depth > 16)
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index b853f084e00166dd6f91a37bef2bbdabe8498baa..7590f49f4f28995bd2a00fc2d27282d25105fd99 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -157,6 +157,10 @@ float byteasfloat(UINT8 fbyte)
 
 static I_Error_t I_Error_GL = NULL;
 
+#ifndef MINI_GL_COMPATIBILITY
+static boolean gl13 = false; // whether we can use opengl 1.3 functions
+#endif
+
 
 // -----------------+
 // DBG_Printf       : Output error messages to debug log if DEBUG_TO_FILE is defined,
@@ -263,6 +267,11 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
 /* GLU functions */
 #define pgluBuild2DMipmaps gluBuild2DMipmaps
 #endif
+#ifndef MINI_GL_COMPATIBILITY
+/* 1.3 functions for multitexturing */
+#define pglActiveTexture, glActiveTexture;
+#define pglMultiTexCoord2f, glMultiTexCoord2f;
+#endif
 #else //!STATIC_OPENGL
 
 /* 1.0 functions */
@@ -387,6 +396,14 @@ static PFNglCopyTexImage2D pglCopyTexImage2D;
 /* GLU functions */
 typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data);
 static PFNgluBuild2DMipmaps pgluBuild2DMipmaps;
+
+#ifndef MINI_GL_COMPATIBILITY
+/* 1.3 functions for multitexturing */
+typedef void (APIENTRY *PFNGLACTIVETEXTUREPROC) (GLenum);
+static PFNGLACTIVETEXTUREPROC pglActiveTexture;
+typedef void (APIENTRY *PFNGLMULTITEXCOORD2FPROC) (GLenum, GLfloat, GLfloat);
+static PFNGLMULTITEXCOORD2FPROC pglMultiTexCoord2f;
+#endif
 #endif
 
 #ifndef MINI_GL_COMPATIBILITY
@@ -402,6 +419,14 @@ static PFNgluBuild2DMipmaps pgluBuild2DMipmaps;
 #define GL_TEXTURE_MAX_LOD 0x813B
 #endif
 
+/* 1.3 GL_TEXTUREi */
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+
 #endif
 
 #ifdef MINI_GL_COMPATIBILITY
@@ -492,6 +517,39 @@ boolean SetupGLfunc(void)
 	return true;
 }
 
+#ifndef MINI_GL_COMPATIBILITY
+// This has to be done after the context is created so the version number can be obtained
+boolean SetupGLFunc13(void)
+{
+#ifndef STATIC_OPENGL
+#define GETOPENGLFUNC(func, proc) \
+	func = GetGLFunc(#proc); \
+	if (!func) \
+	{ \
+		DBG_Printf("failed to get OpenGL function: %s", #proc); \
+	} \
+
+	const char *glversion = (const char *)pglGetString(GL_VERSION);
+	UINT32 majorversion = 0, minorversion = 0;
+
+	if (glversion != NULL && sscanf((char *)glversion, "%u.%u", &majorversion, &minorversion) == 2) // There is a version number I can identify
+	{
+		if (majorversion > 1 || (majorversion == 1 && minorversion >= 3)) // Version of OpenGL is equal to or greater than 1.3
+		{
+			// Get the functions
+			GETOPENGLFUNC(pglActiveTexture , glActiveTexture)
+			GETOPENGLFUNC(pglMultiTexCoord2f , glMultiTexCoord2f)
+
+			gl13 = true; // This is now true, so the new fade mask stuff can be done, if OpenGL version is less than 1.3, it still uses the old fade stuff.
+		}
+	}
+#undef GETOPENGLFUNC
+
+#endif
+	return true;
+}
+#endif
+
 // -----------------+
 // SetNoTexture     : Disable texture
 // -----------------+
@@ -1234,6 +1292,23 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
 				}
 			}
 		}
+		else if (pTexInfo->grInfo.format == GR_TEXFMT_ALPHA_8) // Used for fade masks
+		{
+			const GLubyte *pImgData = (const GLubyte *)pTexInfo->grInfo.data;
+			INT32 i, j;
+
+			for (j = 0; j < h; j++)
+			{
+				for (i = 0; i < w; i++)
+				{
+					tex[w*j+i]  = (pImgData>>4)<<12;
+					tex[w*j+i] |= (255>>4)<<8;
+					tex[w*j+i] |= (255>>4)<<4;
+					tex[w*j+i] |= (255>>4);
+					pImgData++;
+				}
+			}
+		}
 		else
 			DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format);
 #else
@@ -1299,6 +1374,23 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
 				}
 			}
 		}
+		else if (pTexInfo->grInfo.format == GR_TEXFMT_ALPHA_8) // Used for fade masks
+		{
+			const GLubyte *pImgData = (const GLubyte *)pTexInfo->grInfo.data;
+			INT32 i, j;
+
+			for (j = 0; j < h; j++)
+			{
+				for (i = 0; i < w; i++)
+				{
+					tex[w*j+i].s.red   = 255; // 255 because the fade mask is modulated with the screen texture, so alpha affects it while the colours don't
+					tex[w*j+i].s.green = 255;
+					tex[w*j+i].s.blue  = 255;
+					tex[w*j+i].s.alpha = *pImgData;
+					pImgData++;
+				}
+			}
+		}
 		else
 			DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format);
 #endif
@@ -2109,6 +2201,10 @@ EXPORT void HWRAPI(DoScreenWipe)(float alpha)
 	INT32 texsize = 2048;
 	float xfix, yfix;
 
+#ifndef MINI_GL_COMPATIBILITY
+	INT32 fademaskdownloaded = tex_downloaded; // the fade mask that has been set
+#endif
+
 	// Use a power of two texture, dammit
 	if(screen_width <= 1024)
 		texsize = 1024;
@@ -2122,8 +2218,8 @@ EXPORT void HWRAPI(DoScreenWipe)(float alpha)
 
 	SetBlend(PF_Modulated|PF_NoDepthTest|PF_Clip|PF_NoZClip);
 
-	// Draw the screen on bottom to fade to
-	pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
+	// Draw the original screen
+	pglBindTexture(GL_TEXTURE_2D, startScreenWipe);
 	pglBegin(GL_QUADS);
 		pglColor4f(1.0f, 1.0f, 1.0f, 1.0f);
 
@@ -2142,12 +2238,56 @@ EXPORT void HWRAPI(DoScreenWipe)(float alpha)
 		// Bottom right
 		pglTexCoord2f(xfix, 0.0f);
 		pglVertex3f(1.0f, -1.0f, 1.0f);
+
 	pglEnd();
 
 	SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest|PF_Clip|PF_NoZClip);
 
-	// Draw the screen on top that fades.
-	pglBindTexture(GL_TEXTURE_2D, startScreenWipe);
+#ifndef MINI_GL_COMPATIBILITY
+	if (gl13)
+	{
+		// Draw the end screen that fades in
+		pglActiveTexture(GL_TEXTURE0);
+		pglEnable(GL_TEXTURE_2D);
+		pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
+
+		pglActiveTexture(GL_TEXTURE1);
+		pglEnable(GL_TEXTURE_2D);
+		pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded);
+
+		pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+		pglBegin(GL_QUADS);
+			pglColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+			// Bottom left
+			pglMultiTexCoord2f(GL_TEXTURE0, 0.0f, 0.0f);
+			pglMultiTexCoord2f(GL_TEXTURE1, 0.0f, 0.0f);
+			pglVertex3f(-1.0f, -1.0f, 1.0f);
+
+			// Top left
+			pglMultiTexCoord2f(GL_TEXTURE0, 0.0f, yfix);
+			pglMultiTexCoord2f(GL_TEXTURE1, 0.0f, 1.0f);
+			pglVertex3f(-1.0f, 1.0f, 1.0f);
+
+			// Top right
+			pglMultiTexCoord2f(GL_TEXTURE0, xfix, yfix);
+			pglMultiTexCoord2f(GL_TEXTURE1, 1.0f, 1.0f);
+			pglVertex3f(1.0f, 1.0f, 1.0f);
+
+			// Bottom right
+			pglMultiTexCoord2f(GL_TEXTURE0, xfix, 0.0f);
+			pglMultiTexCoord2f(GL_TEXTURE1, 1.0f, 0.0f);
+			pglVertex3f(1.0f, -1.0f, 1.0f);
+		pglEnd();
+
+		pglDisable(GL_TEXTURE_2D); // disable the texture in the 2nd texture unit
+		pglActiveTexture(GL_TEXTURE0);
+	}
+	else
+	{
+#endif
+	// Draw the end screen that fades in
+	pglBindTexture(GL_TEXTURE_2D, endScreenWipe);
 	pglBegin(GL_QUADS);
 		pglColor4f(1.0f, 1.0f, 1.0f, alpha);
 
@@ -2166,8 +2306,10 @@ EXPORT void HWRAPI(DoScreenWipe)(float alpha)
 		// Bottom right
 		pglTexCoord2f(xfix, 0.0f);
 		pglVertex3f(1.0f, -1.0f, 1.0f);
-
 	pglEnd();
+#ifndef MINI_GL_COMPATIBILITY
+	}
+#endif
 
 	tex_downloaded = 0; // 0 so it knows it doesn't have any of the cached patches downloaded right now
 }
diff --git a/src/hardware/r_opengl/r_opengl.h b/src/hardware/r_opengl/r_opengl.h
index fd018f4b13c1b73b92761b88ed137ef6389cff7f..f4d4f77e00bbf4f38ff2bccac7bee579af7e9001 100644
--- a/src/hardware/r_opengl/r_opengl.h
+++ b/src/hardware/r_opengl/r_opengl.h
@@ -35,6 +35,13 @@
 #else
 #include <GL/gl.h>
 #include <GL/glu.h>
+
+#ifndef MINI_GL_COMPATIBILITY
+#ifdef STATIC_OPENGL // Because of the 1.3 functions, you'll need GLext to compile it if static
+#define GL_GLEXT_PROTOTYPES
+#include <GL/glext.h>
+#endif
+#endif
 #endif
 
 #define  _CREATE_DLL_  // necessary for Unix AND Windows
@@ -66,6 +73,7 @@
 boolean LoadGL(void);
 void *GetGLFunc(const char *proc);
 boolean SetupGLfunc(void);
+boolean SetupGLFunc13(void);
 void Flush(void);
 INT32 isExtAvailable(const char *extension, const GLubyte *start);
 boolean SetupPixelFormat(INT32 WantColorBits, INT32 WantStencilBits, INT32 WantDepthBits);