diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index c9490410b478041b66e4a37fe5c4c333bc54cf61..3db5a6207a440c0f434b84ca10b5d3976cbb18ad 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -648,12 +648,15 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 	rsp->eflags = (UINT16)SHORT(players[i].mo->eflags);
 	rsp->flags = LONG(players[i].mo->flags);
 	rsp->flags2 = LONG(players[i].mo->flags2);
+	rsp->renderflags = LONG(players[i].mo->renderflags);
 
 	rsp->radius = LONG(players[i].mo->radius);
 	rsp->height = LONG(players[i].mo->height);
 	rsp->scale = LONG(players[i].mo->scale);
 	rsp->destscale = LONG(players[i].mo->destscale);
 	rsp->scalespeed = LONG(players[i].mo->scalespeed);
+	rsp->spritexscale = LONG(players[i].mo->spritexscale);
+	rsp->spriteyscale = LONG(players[i].mo->spriteyscale);
 }
 
 static void resynch_read_player(resynch_pak *rsp)
@@ -787,6 +790,7 @@ static void resynch_read_player(resynch_pak *rsp)
 	players[i].mo->eflags = (UINT16)SHORT(rsp->eflags);
 	players[i].mo->flags = LONG(rsp->flags);
 	players[i].mo->flags2 = LONG(rsp->flags2);
+	players[i].mo->renderflags = LONG(rsp->renderflags);
 	players[i].mo->friction = LONG(rsp->friction);
 	players[i].mo->health = LONG(rsp->health);
 	players[i].mo->momx = LONG(rsp->momx);
@@ -813,6 +817,8 @@ static void resynch_read_player(resynch_pak *rsp)
 	players[i].mo->scale = LONG(rsp->scale);
 	players[i].mo->destscale = LONG(rsp->destscale);
 	players[i].mo->scalespeed = LONG(rsp->scalespeed);
+	players[i].mo->spritexscale = LONG(rsp->spritexscale);
+	players[i].mo->spriteyscale = LONG(rsp->spriteyscale);
 
 	// And finally, SET THE MOBJ SKIN damn it.
 	if ((players[i].powers[pw_carry] == CR_NIGHTSMODE) && (skins[players[i].skin].sprites[SPR2_NFLY].numframes == 0))
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index adc8a7cc9b090cf4bf5d5881f08070bf34ba1093..0ed0b10f5819386ac9727d6fd7015158ac6b6abd 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -293,12 +293,15 @@ typedef struct
 	UINT32 flags;
 	UINT32 flags2;
 	UINT16 eflags;
+	UINT32 renderflags;
 
 	fixed_t radius;
 	fixed_t height;
 	fixed_t scale;
 	fixed_t destscale;
 	fixed_t scalespeed;
+	fixed_t spritexscale;
+	fixed_t spriteyscale;
 } ATTRPACK resynch_pak;
 
 typedef struct
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 6f4bcdb1d26c89596c39b755dfec1243fbc239a0..7e0c84584b3a79b7714d48da3f058dff68fc027d 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -671,10 +671,6 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_gif_dynamicdelay);
 	CV_RegisterVar(&cv_gif_localcolortable);
 
-#ifdef WALLSPLATS
-	CV_RegisterVar(&cv_splats);
-#endif
-
 	// register these so it is saved to config
 	CV_RegisterVar(&cv_playername);
 	CV_RegisterVar(&cv_playercolor);
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 897c28968e7a42662ad04b533942005fb70f91d2..3cd4b33c546c899f712fca94730c61b39f6dce5c 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -75,9 +75,6 @@ extern consvar_t cv_teamscramble;
 extern consvar_t cv_scrambleonchange;
 
 extern consvar_t cv_netstat;
-#ifdef WALLSPLATS
-extern consvar_t cv_splats;
-#endif
 
 extern consvar_t cv_countdowntime;
 extern consvar_t cv_runscripts;
diff --git a/src/dehacked.c b/src/dehacked.c
index 80a0ad0ff3e77b123ac8c6c0c1c640b89712460b..92a8ffe4e495fc464009bfba708b64d683844e38 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -9035,6 +9035,7 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"AMBUSH",         // Alternate behaviour typically set by MTF_AMBUSH
 	"LINKDRAW",       // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	"SHIELD",         // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
+	"SPLAT",          // Object is a splat
 	NULL
 };
 
@@ -9579,6 +9580,22 @@ struct {
 	{"tr_trans90",tr_trans90},
 	{"NUMTRANSMAPS",NUMTRANSMAPS},
 
+	// Render flags
+	{"RF_HORIZONTALFLIP",RF_HORIZONTALFLIP},
+	{"RF_VERTICALFLIP",RF_VERTICALFLIP},
+	{"RF_ONESIDED",RF_ONESIDED},
+	{"RF_NOSPLATBILLBOARD",RF_NOSPLATBILLBOARD},
+	{"RF_NOSPLATROLLANGLE",RF_NOSPLATROLLANGLE},
+	{"RF_FULLBRIGHT",RF_FULLBRIGHT},
+	{"RF_FULLDARK",RF_FULLDARK},
+	{"RF_SPRITETYPEMASK",RF_SPRITETYPEMASK},
+	{"RF_PAPERSPRITE",RF_PAPERSPRITE},
+	{"RF_FLOORSPRITE",RF_FLOORSPRITE},
+	{"RF_VOXELSPRITE",RF_VOXELSPRITE},
+	{"RF_SHADOWDRAW",RF_SHADOWDRAW},
+	{"RF_SHADOWEFFECTS",RF_SHADOWEFFECTS},
+	{"RF_DROPSHADOW",RF_DROPSHADOW},
+
 	// Level flags
 	{"LF_SCRIPTISFILE",LF_SCRIPTISFILE},
 	{"LF_SPEEDMUSIC",LF_SPEEDMUSIC},
diff --git a/src/doomdef.h b/src/doomdef.h
index d0b7ea0c2391334c703051d02e0dae693dfdfe19..98a22e5ab3d563606163ce5bc2862ff38652a6d1 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -641,6 +641,9 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// Render flats on walls
 #define WALLFLATS
 
+/// Floor splats
+#define FLOORSPLATS
+
 /// Maintain compatibility with older 2.2 demos
 #define OLD22DEMOCOMPAT
 
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index fe0aa2f35b58a6871e8bfc920a846d1067923af9..cfcad29d0634bf27c0d15eb5ca1557df2782e011 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -700,79 +700,6 @@ static void HWR_RenderSkyPlane(extrasubsector_t *xsub, fixed_t fixedheight)
 
 #endif //doplanes
 
-/*
-   wallVerts order is :
-		3--2
-		| /|
-		|/ |
-		0--1
-*/
-#ifdef WALLSPLATS
-static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf)
-{
-	FOutVector wallVerts[4];
-	wallsplat_t *splat;
-	patch_t *gpatch;
-	fixed_t i;
-	// seg bbox
-	fixed_t segbbox[4];
-
-	M_ClearBox(segbbox);
-	M_AddToBox(segbbox,
-		FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x),
-		FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y));
-	M_AddToBox(segbbox,
-		FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x),
-		FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y));
-
-	splat = (wallsplat_t *)gl_curline->linedef->splats;
-	for (; splat; splat = splat->next)
-	{
-		//BP: don't draw splat extern to this seg
-		//    this is quick fix best is explain in logboris.txt at 12-4-2000
-		if (!M_PointInBox(segbbox,splat->v1.x,splat->v1.y) && !M_PointInBox(segbbox,splat->v2.x,splat->v2.y))
-			continue;
-
-		gpatch = W_CachePatchNum(splat->patch, PU_SPRITE);
-		HWR_GetPatch(gpatch);
-
-		wallVerts[0].x = wallVerts[3].x = FIXED_TO_FLOAT(splat->v1.x);
-		wallVerts[0].z = wallVerts[3].z = FIXED_TO_FLOAT(splat->v1.y);
-		wallVerts[2].x = wallVerts[1].x = FIXED_TO_FLOAT(splat->v2.x);
-		wallVerts[2].z = wallVerts[1].z = FIXED_TO_FLOAT(splat->v2.y);
-
-		i = splat->top;
-		if (splat->yoffset)
-			i += *splat->yoffset;
-
-		wallVerts[2].y = wallVerts[3].y = FIXED_TO_FLOAT(i)+(gpatch->height>>1);
-		wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(i)-(gpatch->height>>1);
-
-		wallVerts[3].s = wallVerts[3].t = wallVerts[2].s = wallVerts[0].t = 0.0f;
-		wallVerts[1].s = wallVerts[1].t = wallVerts[2].t = wallVerts[0].s = 1.0f;
-
-		switch (splat->flags & SPLATDRAWMODE_MASK)
-		{
-			case SPLATDRAWMODE_OPAQUE :
-				pSurf.PolyColor.s.alpha = 0xff;
-				i = PF_Translucent;
-				break;
-			case SPLATDRAWMODE_TRANS :
-				pSurf.PolyColor.s.alpha = 128;
-				i = PF_Translucent;
-				break;
-			case SPLATDRAWMODE_SHADE :
-				pSurf.PolyColor.s.alpha = 0xff;
-				i = PF_Substractive;
-				break;
-		}
-
-		HWD.pfnSetShader(2);	// wall shader
-		HWD.pfnDrawPolygon(&pSurf, wallVerts, 4, i|PF_Modulated|PF_Decal);
-	}
-}
-#endif
-
 FBITFIELD HWR_TranstableToAlpha(INT32 transtablenum, FSurfaceInfo *pSurf)
 {
 	switch (transtablenum)
@@ -797,19 +724,21 @@ static void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, I
 // Wall generation from subsector segs
 // ==========================================================================
 
+/*
+   wallVerts order is :
+		3--2
+		| /|
+		|/ |
+		0--1
+*/
+
 //
 // HWR_ProjectWall
 //
 static void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blendmode, INT32 lightlevel, extracolormap_t *wallcolormap)
 {
 	HWR_Lighting(pSurf, lightlevel, wallcolormap);
-
 	HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, 2, false); // wall shader
-
-#ifdef WALLSPLATS
-	if (gl_curline->linedef->splats && cv_splats.value)
-		HWR_DrawSegsSplats(pSurf);
-#endif
 }
 
 // ==========================================================================
@@ -3977,7 +3906,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 	patch_t *gpatch; // sprite patch converted to hardware
 	FSurfaceInfo Surf;
 	const boolean hires = (spr->mobj && spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES);
-	//const boolean papersprite = (spr->mobj && (spr->mobj->frame & FF_PAPERSPRITE));
+	//const boolean papersprite = R_ThingIsPaperSprite(spr->mobj);
 	if (spr->mobj)
 		this_scale = FIXED_TO_FLOAT(spr->mobj->scale);
 	if (hires)
@@ -4761,14 +4690,14 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	size_t lumpoff;
 	unsigned rot;
 	UINT16 flip;
-	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
+	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing));
 	boolean mirrored = thing->mirrored;
-	boolean hflip = (!(thing->frame & FF_HORIZONTALFLIP) != !mirrored);
+	boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
 	INT32 dispoffset;
 
 	angle_t ang;
 	INT32 heightsec, phs;
-	const boolean papersprite = (thing->frame & FF_PAPERSPRITE);
+	const boolean papersprite = R_ThingIsPaperSprite(thing);
 	angle_t mobjangle = (thing->player ? thing->player->drawangle : thing->angle);
 	float z1, z2;
 
@@ -4909,13 +4838,16 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	if (thing->rollangle)
 	{
 		rollangle = R_GetRollAngle(thing->rollangle);
-		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, sprinfo, rollangle);
+		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
+
 		if (rotsprite != NULL)
 		{
-			spr_width = SHORT(rotsprite->width) << FRACBITS;
-			spr_height = SHORT(rotsprite->height) << FRACBITS;
-			spr_offset = SHORT(rotsprite->leftoffset) << FRACBITS;
-			spr_topoffset = SHORT(rotsprite->topoffset) << FRACBITS;
+			spr_width = rotsprite->width << FRACBITS;
+			spr_height = rotsprite->height << FRACBITS;
+			spr_offset = rotsprite->leftoffset << FRACBITS;
+			spr_topoffset = rotsprite->topoffset << FRACBITS;
+			spr_topoffset += FEETADJUST;
+
 			// flip -> rotate, not rotate -> flip
 			flip = 0;
 		}
@@ -6224,13 +6156,7 @@ void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend,
 	}
 
 	blendmode |= PF_Modulated;	// No PF_Occlude means overlapping (incorrect) transparency
-
 	HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode, shader, false);
-
-#ifdef WALLSPLATS
-	if (gl_curline->linedef->splats && cv_splats.value)
-		HWR_DrawSegsSplats(pSurf);
-#endif
 }
 
 INT32 HWR_GetTextureUsed(void)
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 335e6cfbc720f85298e44bf6280972bf1382c5d5..7b290bf3ff3a87d8e9d80f14c9c1c84c29a91e15 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -458,7 +458,7 @@ static int libd_getSpritePatch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &spriteinfo[i], rot);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), true, &spriteinfo[i], rot);
 			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
@@ -570,7 +570,7 @@ static int libd_getSprite2Patch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &skins[i].sprinfo[j], rot);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), true, &skins[i].sprinfo[j], rot);
 			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index a1a3e96c93afd9d036ad6f8120e6a1c0bf82b9ac..c390eb064c08a2f8f98a87fccfdfa02fd6c236bc 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -56,6 +56,7 @@ enum mobj_e {
 	mobj_flags,
 	mobj_flags2,
 	mobj_eflags,
+	mobj_renderflags,
 	mobj_skin,
 	mobj_color,
 	mobj_bnext,
@@ -83,6 +84,8 @@ enum mobj_e {
 	mobj_scale,
 	mobj_destscale,
 	mobj_scalespeed,
+	mobj_spritexscale,
+	mobj_spriteyscale,
 	mobj_extravalue1,
 	mobj_extravalue2,
 	mobj_cusval,
@@ -125,6 +128,7 @@ static const char *const mobj_opt[] = {
 	"flags",
 	"flags2",
 	"eflags",
+	"renderflags",
 	"skin",
 	"color",
 	"bnext",
@@ -152,6 +156,8 @@ static const char *const mobj_opt[] = {
 	"scale",
 	"destscale",
 	"scalespeed",
+	"spritexscale",
+	"spriteyscale",
 	"extravalue1",
 	"extravalue2",
 	"cusval",
@@ -277,6 +283,9 @@ static int mobj_get(lua_State *L)
 	case mobj_eflags:
 		lua_pushinteger(L, mo->eflags);
 		break;
+	case mobj_renderflags:
+		lua_pushinteger(L, mo->renderflags);
+		break;
 	case mobj_skin: // skin name or nil, not struct
 		if (!mo->skin)
 			return 0;
@@ -381,6 +390,12 @@ static int mobj_get(lua_State *L)
 	case mobj_scalespeed:
 		lua_pushfixed(L, mo->scalespeed);
 		break;
+	case mobj_spritexscale:
+		lua_pushfixed(L, mo->spritexscale);
+		break;
+	case mobj_spriteyscale:
+		lua_pushfixed(L, mo->spriteyscale);
+		break;
 	case mobj_extravalue1:
 		lua_pushinteger(L, mo->extravalue1);
 		break;
@@ -580,6 +595,9 @@ static int mobj_set(lua_State *L)
 	case mobj_eflags:
 		mo->eflags = (UINT32)luaL_checkinteger(L, 3);
 		break;
+	case mobj_renderflags:
+		mo->renderflags = (UINT32)luaL_checkinteger(L, 3);
+		break;
 	case mobj_skin: // set skin by name
 	{
 		INT32 i;
@@ -721,6 +739,12 @@ static int mobj_set(lua_State *L)
 	case mobj_scalespeed:
 		mo->scalespeed = luaL_checkfixed(L, 3);
 		break;
+	case mobj_spritexscale:
+		mo->spritexscale = luaL_checkfixed(L, 3);
+		break;
+	case mobj_spriteyscale:
+		mo->spriteyscale = luaL_checkfixed(L, 3);
+		break;
 	case mobj_extravalue1:
 		mo->extravalue1 = luaL_checkinteger(L, 3);
 		break;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index de4385fa7d6618cf72dc7fc2aa53f1c590d41019..827650ce6c11ae500b8e2adda51304dfc717bc93 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -38,10 +38,6 @@
 static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
 consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
 
-#ifdef WALLSPLATS
-consvar_t cv_splats = CVAR_INIT ("splats", "On", CV_SAVE, CV_OnOff, NULL);
-#endif
-
 actioncache_t actioncachehead;
 
 static mobj_t *overlaycap = NULL;
@@ -1961,29 +1957,6 @@ void P_XYMovement(mobj_t *mo)
 				return;
 			}
 
-			// draw damage on wall
-			//SPLAT TEST ----------------------------------------------------------
-#ifdef WALLSPLATS
-			if (blockingline && mo->type != MT_REDRING && mo->type != MT_FIREBALL
-			&& !(mo->flags2 & (MF2_AUTOMATIC|MF2_RAILRING|MF2_BOUNCERING|MF2_EXPLOSION|MF2_SCATTER)))
-				// set by last P_TryMove() that failed
-			{
-				divline_t divl;
-				divline_t misl;
-				fixed_t frac;
-
-				P_MakeDivline(blockingline, &divl);
-				misl.x = mo->x;
-				misl.y = mo->y;
-				misl.dx = mo->momx;
-				misl.dy = mo->momy;
-				frac = P_InterceptVector(&divl, &misl);
-				R_AddWallSplat(blockingline, P_PointOnLineSide(mo->x,mo->y,blockingline),
-					"A_DMG3", mo->z, frac, SPLATDRAWMODE_SHADE);
-			}
-#endif
-			// --------------------------------------------------------- SPLAT TEST
-
 			P_ExplodeMissile(mo);
 			return;
 		}
@@ -9614,12 +9587,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 			mobj->fuse = 1; // Return to base.
 		break;
 	}
-	case MT_CANNONBALL:
-#ifdef FLOORSPLATS
-		R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x,
-			mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
-#endif
-		break;
 	case MT_SPINDUST: // Spindash dust
 		mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
 		mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
@@ -10487,6 +10454,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
+	mobj->renderflags = 0;
 	P_SetupStateAnimation(mobj, st);
 
 	mobj->friction = ORIG_FRICTION;
@@ -10497,6 +10465,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	mobj->scale = FRACUNIT;
 	mobj->destscale = mobj->scale;
 	mobj->scalespeed = FRACUNIT/12;
+	mobj->spritexscale = mobj->spriteyscale = mobj->scale;
 
 	// TODO: Make this a special map header
 	if ((maptol & TOL_ERZ3) && !(mobj->type == MT_BLACKEGGMAN))
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 27a6ef4f05d3a2f8a5a9cf57b8fe495fe4325ff0..eb8a9ccc267dba28c0bbe51eeab536cda5f7e906 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -194,6 +194,7 @@ typedef enum
 	MF2_AMBUSH         = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
 	MF2_LINKDRAW       = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	MF2_SHIELD         = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
+	MF2_SPLAT          = 1<<30, // Renders as a splat
 	// free: to and including 1<<31
 } mobjflag2_t;
 
@@ -308,6 +309,7 @@ typedef struct mobj_s
 	UINT32 flags; // flags from mobjinfo tables
 	UINT32 flags2; // MF2_ flags
 	UINT16 eflags; // extra flags
+	UINT32 renderflags; // render flags
 
 	void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin)
 	// Player and mobj sprites in multiplayer modes are modified
@@ -360,6 +362,7 @@ typedef struct mobj_s
 	fixed_t scale;
 	fixed_t destscale;
 	fixed_t scalespeed;
+	fixed_t spritexscale, spriteyscale;
 
 	// Extra values are for internal use for whatever you want
 	INT32 extravalue1;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 4f6f31803998d8cf8c19472a21d4090d779b2119..d613efd7027d36be5c2e45f6eb76c3bcec1fe73e 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1393,7 +1393,10 @@ typedef enum
 	MD2_COLORIZED    = 1<<12,
 	MD2_MIRRORED     = 1<<13,
 	MD2_ROLLANGLE    = 1<<14,
-	MD2_SHADOWSCALE  = 1<<15,
+	MD2_SPRITEXSCALE = 1<<15,
+	MD2_SPRITEYSCALE = 1<<16,
+	MD2_SHADOWSCALE  = 1<<17,
+	MD2_RENDERFLAGS  = 1<<18,
 } mobj_diff2_t;
 
 typedef enum
@@ -1604,8 +1607,14 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		diff2 |= MD2_MIRRORED;
 	if (mobj->rollangle)
 		diff2 |= MD2_ROLLANGLE;
+	if (mobj->spritexscale != FRACUNIT)
+		diff2 |= MD2_SPRITEXSCALE;
+	if (mobj->spriteyscale != FRACUNIT)
+		diff2 |= MD2_SPRITEYSCALE;
 	if (mobj->shadowscale)
 		diff2 |= MD2_SHADOWSCALE;
+	if (mobj->renderflags)
+		diff2 |= MD2_RENDERFLAGS;
 	if (diff2 != 0)
 		diff |= MD_MORE;
 
@@ -1746,8 +1755,14 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEUINT8(save_p, mobj->mirrored);
 	if (diff2 & MD2_ROLLANGLE)
 		WRITEANGLE(save_p, mobj->rollangle);
+	if (diff2 & MD2_SPRITEXSCALE)
+		WRITEFIXED(save_p, mobj->spritexscale);
+	if (diff2 & MD2_SPRITEYSCALE)
+		WRITEFIXED(save_p, mobj->spriteyscale);
 	if (diff2 & MD2_SHADOWSCALE)
 		WRITEFIXED(save_p, mobj->shadowscale);
+	if (diff2 & MD2_RENDERFLAGS)
+		WRITEUINT32(save_p, mobj->renderflags);
 
 	WRITEUINT32(save_p, mobj->mobjnum);
 }
@@ -2755,8 +2770,14 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->mirrored = READUINT8(save_p);
 	if (diff2 & MD2_ROLLANGLE)
 		mobj->rollangle = READANGLE(save_p);
+	if (diff2 & MD2_SPRITEXSCALE)
+		mobj->spritexscale = READFIXED(save_p);
+	if (diff2 & MD2_SPRITEYSCALE)
+		mobj->spriteyscale = READFIXED(save_p);
 	if (diff2 & MD2_SHADOWSCALE)
 		mobj->shadowscale = READFIXED(save_p);
+	if (diff2 & MD2_RENDERFLAGS)
+		mobj->renderflags = READUINT32(save_p);
 
 	if (diff & MD_REDFLAG)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index 4dbb70e0c46abb81885500559788a9c9e383e932..349b0582d99952c9f2ecd847c79b0a589a114de5 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1080,9 +1080,6 @@ static void P_InitializeLinedef(line_t *ld)
 	ld->frontsector = ld->backsector = NULL;
 
 	ld->validcount = 0;
-#ifdef WALLSPLATS
-	ld->splats = NULL;
-#endif
 	ld->firsttag = ld->nexttag = -1;
 	ld->polyobj = NULL;
 
@@ -2081,9 +2078,6 @@ static boolean P_LoadMapData(const virtres_t *virt)
 static void P_InitializeSubsector(subsector_t *ss)
 {
 	ss->sector = NULL;
-#ifdef FLOORSPLATS
-	ss->splats = NULL;
-#endif
 	ss->validcount = 0;
 }
 
@@ -2128,7 +2122,7 @@ static void P_LoadNodes(UINT8 *data)
   * \param seg Seg to compute length for.
   * \return Length in fracunits.
   */
-fixed_t P_SegLength(seg_t *seg)
+static fixed_t P_SegLength(seg_t *seg)
 {
 	INT64 dx = (seg->v2->x - seg->v1->x)>>1;
 	INT64 dy = (seg->v2->y - seg->v1->y)>>1;
@@ -4094,11 +4088,6 @@ boolean P_LoadLevel(boolean fromnetsave)
 	Patch_FreeTag(PU_PATCH_ROTATED);
 	Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
 
-#if defined (WALLSPLATS) || defined (FLOORSPLATS)
-	// clear the splats from previous level
-	R_ClearLevelSplats();
-#endif
-
 	P_InitThinkers();
 	P_InitCachedActions();
 
diff --git a/src/p_user.c b/src/p_user.c
index 7bc45bfc43eae2d928f15733b526d00103b45994..0341128d53470ca2d6b3d0701028d779f62e548a 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8678,12 +8678,6 @@ void P_MovePlayer(player_t *player)
 		player->fovadd = 0;
 #endif
 
-#ifdef FLOORSPLATS
-	if (cv_shadow.value && rendermode == render_soft)
-		R_AddFloorSplat(player->mo->subsector, player->mo, "SHADOW", player->mo->x,
-			player->mo->y, player->mo->floorz, SPLATDRAWMODE_OPAQUE);
-#endif
-
 	// Look for blocks to bust up
 	// Because of FF_SHATTER, we should look for blocks constantly,
 	// not just when spinning or playing as Knuckles
diff --git a/src/r_bsp.c b/src/r_bsp.c
index a430ef04069446eb0aee9452e6acb737ae4332f5..d2c8e9f86a8357daa1cb91a4446f2ab293eeb6bf 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -1048,11 +1048,6 @@ static void R_Subsector(size_t num)
 		}
 	}
 
-#ifdef FLOORSPLATS
-	if (sub->splats)
-		R_AddVisibleFloorSplats(sub);
-#endif
-
    // killough 9/18/98: Fix underwater slowdown, by passing real sector
    // instead of fake one. Improve sprite lighting by basing sprite
    // lightlevels on floor & ceiling lightlevels in the surrounding area.
diff --git a/src/r_defs.h b/src/r_defs.h
index 4dfba0691f4d183c9c441fbeb7e62fa3b26d651d..d32313a462a3642d4ad19e2c3128a1ce28ffa317 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -409,9 +409,6 @@ typedef struct line_s
 	sector_t *backsector;
 
 	size_t validcount; // if == validcount, already checked
-#if 1//#ifdef WALLSPLATS
-	void *splats; // wallsplat_t list
-#endif
 	INT32 firsttag, nexttag; // improves searches for tags.
 	polyobj_t *polyobj; // Belongs to a polyobject?
 
@@ -457,9 +454,6 @@ typedef struct subsector_s
 	INT16 numlines;
 	UINT16 firstline;
 	struct polyobj_s *polyList; // haleyjd 02/19/06: list of polyobjects
-#if 1//#ifdef FLOORSPLATS
-	void *splats; // floorsplat_t list
-#endif
 	size_t validcount;
 } subsector_t;
 
@@ -674,6 +668,7 @@ typedef struct
 	UINT8 *columns; // Software column data
 
 	void *hardware; // OpenGL patch, allocated whenever necessary
+	void *flats[4]; // The patch as flats
 
 #ifdef ROTSPRITE
 	rotsprite_t *rotated; // Rotated patches
@@ -718,6 +713,28 @@ typedef struct
 #pragma pack()
 #endif
 
+typedef enum
+{
+	RF_HORIZONTALFLIP   = 0x0001,   // Flip sprite horizontally
+	RF_VERTICALFLIP     = 0x0002,   // Flip sprite vertically
+	RF_ONESIDED         = 0x0004,   // Wall/floor sprite is visible from front only
+	RF_NOSPLATBILLBOARD = 0x0008,   // Don't billboard floor sprites (faces forward from the view angle)
+	RF_NOSPLATROLLANGLE = 0x0010,   // Don't rotate floor sprites by the object's rollangle (uses rotated patches instead)
+
+	RF_BLENDMASK        = 0x0F00,   // --Blending modes
+	RF_FULLBRIGHT       = 0x0100,   // Sprite is drawn at full brightness
+	RF_FULLDARK         = 0x0200,   // Sprite is drawn completely dark
+
+	RF_SPRITETYPEMASK   = 0x7000,   // ---Different sprite types, not all implemented
+	RF_PAPERSPRITE      = 0x1000,   // Paper sprite
+	RF_FLOORSPRITE      = 0x2000,   // Floor sprite
+	RF_VOXELSPRITE      = 0x3000,   // Voxel object
+
+	RF_SHADOWDRAW       = 0x10000,  // Stretches and skews the sprite like a shadow.
+	RF_SHADOWEFFECTS    = 0x20000,  // Scales and becomes transparent like a shadow.
+	RF_DROPSHADOW       = (RF_SHADOWDRAW | RF_SHADOWEFFECTS | RF_FULLDARK),
+} renderflags_t;
+
 typedef enum
 {
 	SRF_SINGLE      = 0,   // 0-angle for all rotations
@@ -759,7 +776,7 @@ typedef struct
 	UINT16 flip;
 
 #ifdef ROTSPRITE
-	rotsprite_t *rotated[16]; // Rotated patches
+	rotsprite_t *rotated[2][16]; // Rotated patches
 #endif
 } spriteframe_t;
 
diff --git a/src/r_draw.c b/src/r_draw.c
index 2b798c3bf383c42d22e99674e1ceb4521a85c352..e10d6e399d2fe5c80b85132a0d3ac9fbfdd98502 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -98,6 +98,7 @@ INT32 dc_numlights = 0, dc_maxlights, dc_texheight;
 
 INT32 ds_y, ds_x1, ds_x2;
 lighttable_t *ds_colormap;
+lighttable_t *ds_translation; // Lactozilla: Sprite splat drawer
 fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep;
 UINT16 ds_flatwidth, ds_flatheight;
 boolean ds_powersoftwo;
diff --git a/src/r_draw.h b/src/r_draw.h
index 1ca22f18aa7f80840c9c367dcb2c0199bb62a5c0..d77bbedd306bb4cf101336daead31e23ab071c9b 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -56,6 +56,7 @@ extern INT32 dc_texheight;
 
 extern INT32 ds_y, ds_x1, ds_x2;
 extern lighttable_t *ds_colormap;
+extern lighttable_t *ds_translation;
 extern fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep;
 extern UINT16 ds_flatwidth, ds_flatheight;
 extern boolean ds_powersoftwo;
@@ -151,8 +152,10 @@ void R_DrawColumnShadowed_8(void);
 
 void R_DrawSpan_8(void);
 void R_DrawSplat_8(void);
+void R_DrawFloorSprite_8(void);
 void R_DrawTranslucentSpan_8(void);
 void R_DrawTranslucentSplat_8(void);
+void R_DrawTranslucentFloorSprite_8(void);
 void R_DrawTiltedSpan_8(void);
 void R_DrawTiltedTranslucentSpan_8(void);
 #ifndef NOWATER
@@ -171,8 +174,10 @@ void R_DrawFogSpan_8(void);
 // Lactozilla: Non-powers-of-two
 void R_DrawSpan_NPO2_8(void);
 void R_DrawTranslucentSpan_NPO2_8(void);
+void R_DrawFloorSprite_NPO2_8(void);
 void R_DrawSplat_NPO2_8(void);
 void R_DrawTranslucentSplat_NPO2_8(void);
+void R_DrawTranslucentFloorSprite_NPO2_8(void);
 void R_DrawTiltedSpan_NPO2_8(void);
 void R_DrawTiltedTranslucentSpan_NPO2_8(void);
 #ifndef NOWATER
diff --git a/src/r_draw8.c b/src/r_draw8.c
index 940ea724b31ef2411514799d4a9f7df7e284c907..bc39e91a5f5d797cfff40af4df14796a37a93560 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -1419,6 +1419,230 @@ void R_DrawTranslucentSplat_8 (void)
 	}
 }
 
+/**	\brief The R_DrawFloorSprite_8 function
+	Just like R_DrawSplat_8, but for floor sprites.
+*/
+void R_DrawFloorSprite_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT16 *source;
+	UINT8 *colormap;
+	UINT8 *translation;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	// SoM: we only need 6 bits for the integer part (0 thru 63) so the rest
+	// can be used for the fraction part. This allows calculation of the memory address in the
+	// texture with two shifts, an OR and one AND. (see below)
+	// for texture sizes > 64 the amount of precision we can allow will decrease, but only by one
+	// bit per power of two (obviously)
+	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
+	// than the original span renderer. Whodathunkit?
+	xposition <<= nflatshiftup; yposition <<= nflatshiftup;
+	xstep <<= nflatshiftup; ystep <<= nflatshiftup;
+
+	source = (UINT16 *)ds_source;
+	colormap = ds_colormap;
+	translation = ds_translation;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count >= 8)
+	{
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		//
+		// <Callum> 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size)
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[0] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[1] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[2] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[3] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[4] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[5] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[6] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val & 0xFF00)
+			dest[7] = colormap[translation[val & 0xFF]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest += 8;
+		count -= 8;
+	}
+	while (count-- && dest <= deststop)
+	{
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			*dest = colormap[translation[val & 0xFF]];
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+/**	\brief The R_DrawTranslucentFloorSplat_8 function
+	Just like R_DrawFloorSprite_8, but is translucent!
+*/
+void R_DrawTranslucentFloorSprite_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT16 *source;
+	UINT8 *colormap;
+	UINT8 *translation;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	// SoM: we only need 6 bits for the integer part (0 thru 63) so the rest
+	// can be used for the fraction part. This allows calculation of the memory address in the
+	// texture with two shifts, an OR and one AND. (see below)
+	// for texture sizes > 64 the amount of precision we can allow will decrease, but only by one
+	// bit per power of two (obviously)
+	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
+	// than the original span renderer. Whodathunkit?
+	xposition <<= nflatshiftup; yposition <<= nflatshiftup;
+	xstep <<= nflatshiftup; ystep <<= nflatshiftup;
+
+	source = (UINT16 *)ds_source;
+	colormap = ds_colormap;
+	translation = ds_translation;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count >= 8)
+	{
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[0] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[0]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[1] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[1]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[2] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[2]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[3] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[3]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[4] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[4]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[5] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[5]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[6] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[6]);
+		xposition += xstep;
+		yposition += ystep;
+
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			dest[7] = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + dest[7]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest += 8;
+		count -= 8;
+	}
+	while (count-- && dest <= deststop)
+	{
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val & 0xFF00)
+			*dest = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + *dest);
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
 /**	\brief The R_DrawTranslucentSpan_8 function
 	Draws the actual span with translucency.
 */
diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c
index 02015569455e94df9e4ec9a8c8be835cbd0da856..087a8d30caf547501571347f9a02dc11f74fc89e 100644
--- a/src/r_draw8_npo2.c
+++ b/src/r_draw8_npo2.c
@@ -754,6 +754,104 @@ void R_DrawTranslucentSplat_NPO2_8 (void)
 	}
 }
 
+/**	\brief The R_DrawFloorSprite_NPO2_8 function
+	Just like R_DrawSplat_NPO2_8, but for floor sprites.
+*/
+void R_DrawFloorSprite_NPO2_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT16 *source;
+	UINT8 *translation;
+	UINT8 *colormap;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = (UINT16 *)ds_source;
+	colormap = ds_colormap;
+	translation = ds_translation;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		val = source[((y * ds_flatwidth) + x)];
+		if (val & 0xFF00)
+			*dest = colormap[translation[val & 0xFF]];
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+/**	\brief The R_DrawTranslucentFloorSprite_NPO2_8 function
+	Just like R_DrawFloorSprite_NPO2_8, but is translucent!
+*/
+void R_DrawTranslucentFloorSprite_NPO2_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT16 *source;
+	UINT8 *translation;
+	UINT8 *colormap;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = (UINT16 *)ds_source;
+	colormap = ds_colormap;
+	translation = ds_translation;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		val = source[((y * ds_flatwidth) + x)];
+		if (val & 0xFF00)
+			*dest = *(ds_transmap + (colormap[translation[val & 0xFF]] << 8) + *dest);
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
 /**	\brief The R_DrawTranslucentSpan_NPO2_8 function
 	Draws the actual span with translucency.
 */
diff --git a/src/r_main.c b/src/r_main.c
index 3568e211644f0d2b0b9f10a68020950626fe9599..03f9c68185f269ef85dc6cb37abdff18345221c4 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -1472,9 +1472,6 @@ void R_RenderPlayerView(player_t *player)
 	}
 	R_ClearDrawSegs();
 	R_ClearSprites();
-#ifdef FLOORSPLATS
-	R_ClearVisibleFloorSplats();
-#endif
 	Portal_InitList();
 
 	// check for new console commands.
@@ -1555,9 +1552,6 @@ void R_RenderPlayerView(player_t *player)
 
 	rs_sw_planetime = I_GetTimeMicros();
 	R_DrawPlanes();
-#ifdef FLOORSPLATS
-	R_DrawVisibleFloorSplats();
-#endif
 	rs_sw_planetime = I_GetTimeMicros() - rs_sw_planetime;
 
 	// draw mid texture and sprite
diff --git a/src/r_patch.c b/src/r_patch.c
index 9ca04dd55e60506c9d0fe31d1c2cdf9466d1e185..9b1e7d1b8caac9cb21bae9659c2095e09589434a 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -11,6 +11,7 @@
 
 #include "doomdef.h"
 #include "r_patch.h"
+#include "r_picformats.h"
 #include "r_defs.h"
 #include "z_zone.h"
 
@@ -66,18 +67,25 @@ patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest)
 
 static void Patch_FreeData(patch_t *patch)
 {
+	INT32 i;
+
 #ifdef HWRENDER
 	if (patch->hardware)
 		HWR_FreeTexture(patch);
 #endif
 
+	for (i = 0; i < 2; i++)
+	{
+		if (patch->flats[i])
+			Patch_Free(patch->flats[i]);
+	}
+
 #ifdef ROTSPRITE
 	if (patch->rotated)
 	{
 		rotsprite_t *rotsprite = patch->rotated;
-		INT32 i = 0;
 
-		for (; i < rotsprite->angles; i++)
+		for (i = 0; i < rotsprite->angles; i++)
 		{
 			if (rotsprite->patches[i])
 				Patch_Free(rotsprite->patches[i]);
@@ -116,6 +124,13 @@ void Patch_FreeTags(INT32 lowtag, INT32 hightag)
 	Z_IterateTags(lowtag, hightag, Patch_FreeTagsCallback);
 }
 
+void Patch_GenerateFlat(patch_t *patch, pictureflags_t flags)
+{
+	UINT8 flip = (flags & (PICFLAGS_XFLIP | PICFLAGS_YFLIP));
+	if (patch->flats[flip] == NULL)
+		patch->flats[flip] = Picture_Convert(PICFMT_PATCH, patch, PICFMT_FLAT16, 0, NULL, 0, 0, 0, 0, flags);
+}
+
 #ifdef HWRENDER
 //
 // Allocates a hardware patch.
diff --git a/src/r_patch.h b/src/r_patch.h
index 6991d3637733ce70b68ed9c97b8ad45859f1f2bf..32bcb3909efe057af98d54cd151f56414c71deb1 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -13,6 +13,7 @@
 #define __R_PATCH__
 
 #include "r_defs.h"
+#include "r_picformats.h"
 #include "doomdef.h"
 
 // Patch functions
@@ -22,6 +23,8 @@ void Patch_Free(patch_t *patch);
 #define Patch_FreeTag(tagnum) Patch_FreeTags(tagnum, tagnum)
 void Patch_FreeTags(INT32 lowtag, INT32 hightag);
 
+void Patch_GenerateFlat(patch_t *patch, pictureflags_t flags);
+
 #ifdef HWRENDER
 void *Patch_AllocateHardwarePatch(patch_t *patch);
 void *Patch_CreateGL(patch_t *patch);
@@ -30,7 +33,12 @@ void *Patch_CreateGL(patch_t *patch);
 #ifdef ROTSPRITE
 void Patch_Rotate(patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip);
 patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip);
-patch_t *Patch_GetRotatedSprite(spriteframe_t *sprite, size_t frame, size_t spriteangle, boolean flip, void *info, INT32 rotationangle);INT32 R_GetRollAngle(angle_t rollangle);
+patch_t *Patch_GetRotatedSprite(
+	spriteframe_t *sprite,
+	size_t frame, size_t spriteangle,
+	boolean flip, boolean adjustfeet,
+	void *info, INT32 rotationangle);
+INT32 R_GetRollAngle(angle_t rollangle);
 #endif
 
 #endif // __R_PATCH__
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index 98e3a7687f37ddd4e348947d6fa7d9da5dac6d61..123c4eef229a20fa554094bf44a2cc3853e72dc8 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -41,19 +41,26 @@ patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip)
 	return rotsprite->patches[angle];
 }
 
-patch_t *Patch_GetRotatedSprite(spriteframe_t *sprite, size_t frame, size_t spriteangle, boolean flip, void *info, INT32 rotationangle)
+patch_t *Patch_GetRotatedSprite(
+	spriteframe_t *sprite,
+	size_t frame, size_t spriteangle,
+	boolean flip, boolean adjustfeet,
+	void *info, INT32 rotationangle)
 {
-	rotsprite_t *rotsprite = sprite->rotated[spriteangle];
+	rotsprite_t *rotsprite;
 	spriteinfo_t *sprinfo = (spriteinfo_t *)info;
 	INT32 idx = rotationangle;
+	UINT8 type = (adjustfeet ? 1 : 0);
 
 	if (rotationangle < 1 || rotationangle >= ROTANGLES)
 		return NULL;
 
+	rotsprite = sprite->rotated[type][spriteangle];
+
 	if (rotsprite == NULL)
 	{
 		rotsprite = RotatedPatch_Create(ROTANGLES);
-		sprite->rotated[spriteangle] = rotsprite;
+		sprite->rotated[type][spriteangle] = rotsprite;
 	}
 
 	if (flip)
@@ -84,7 +91,8 @@ patch_t *Patch_GetRotatedSprite(spriteframe_t *sprite, size_t frame, size_t spri
 		RotatedPatch_DoRotation(rotsprite, patch, rotationangle, xpivot, ypivot, flip);
 
 		//BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
-		((patch_t *)rotsprite->patches[idx])->topoffset += FEETADJUST>>FRACBITS;
+		if (adjustfeet)
+			((patch_t *)rotsprite->patches[idx])->topoffset += FEETADJUST>>FRACBITS;
 	}
 
 	return rotsprite->patches[idx];
diff --git a/src/r_plane.c b/src/r_plane.c
index 6c238896ca4f867d1c49b9cfee4260de2f8315d9..797919a9fb9e0636ef9453e71b652862f8459575 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -745,6 +745,7 @@ void R_DrawSinglePlane(visplane_t *pl)
 	ffloor_t *rover;
 	int type;
 	int spanfunctype = BASEDRAWFUNC;
+	angle_t viewang = viewangle;
 
 	if (!(pl->minx <= pl->maxx))
 		return;
@@ -871,20 +872,6 @@ void R_DrawSinglePlane(visplane_t *pl)
 			light = (pl->lightlevel >> LIGHTSEGSHIFT);
 	}
 
-	if (!pl->slope // Don't mess with angle on slopes! We'll handle this ourselves later
-		&& viewangle != pl->viewangle+pl->plangle)
-	{
-		memset(cachedheight, 0, sizeof (cachedheight));
-		angle = (pl->viewangle+pl->plangle-ANGLE_90)>>ANGLETOFINESHIFT;
-		basexscale = FixedDiv(FINECOSINE(angle),centerxfrac);
-		baseyscale = -FixedDiv(FINESINE(angle),centerxfrac);
-		viewangle = pl->viewangle+pl->plangle;
-	}
-
-	xoffs = pl->xoffs;
-	yoffs = pl->yoffs;
-	planeheight = abs(pl->height - pl->viewz);
-
 	currentplane = pl;
 	levelflat = &levelflats[pl->picnum];
 
@@ -909,6 +896,20 @@ void R_DrawSinglePlane(visplane_t *pl)
 				R_CheckFlatLength(ds_flatwidth * ds_flatheight);
 	}
 
+	if (!pl->slope // Don't mess with angle on slopes! We'll handle this ourselves later
+		&& viewangle != pl->viewangle+pl->plangle)
+	{
+		memset(cachedheight, 0, sizeof (cachedheight));
+		angle = (pl->viewangle+pl->plangle-ANGLE_90)>>ANGLETOFINESHIFT;
+		basexscale = FixedDiv(FINECOSINE(angle),centerxfrac);
+		baseyscale = -FixedDiv(FINESINE(angle),centerxfrac);
+		viewangle = pl->viewangle+pl->plangle;
+	}
+
+	xoffs = pl->xoffs;
+	yoffs = pl->yoffs;
+	planeheight = abs(pl->height - pl->viewz);
+
 	if (light >= LIGHTLEVELS)
 		light = LIGHTLEVELS-1;
 
@@ -1121,6 +1122,8 @@ using the palette colors.
 		}
 	}
 #endif
+
+	viewangle = viewang;
 }
 
 void R_PlaneBounds(visplane_t *plane)
diff --git a/src/r_segs.c b/src/r_segs.c
index 1778278927c70df82c7393c048c3ff577b2d52aa..77425d34510fd53e945761661ec862f298135060 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -63,170 +63,6 @@ static lighttable_t **walllights;
 static INT16 *maskedtexturecol;
 static fixed_t *maskedtextureheight = NULL;
 
-// ==========================================================================
-// R_Splats Wall Splats Drawer
-// ==========================================================================
-
-#ifdef WALLSPLATS
-static INT16 last_ceilingclip[MAXVIDWIDTH];
-static INT16 last_floorclip[MAXVIDWIDTH];
-
-static void R_DrawSplatColumn(column_t *column)
-{
-	INT32 topscreen, bottomscreen;
-	fixed_t basetexturemid;
-	INT32 topdelta, prevdelta = -1;
-
-	basetexturemid = dc_texturemid;
-
-	for (; column->topdelta != 0xff ;)
-	{
-		// calculate unclipped screen coordinates for post
-		topdelta = column->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topscreen = sprtopscreen + spryscale*topdelta;
-		bottomscreen = topscreen + spryscale*column->length;
-
-		dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;
-		dc_yh = (bottomscreen-1)>>FRACBITS;
-
-		if (dc_yh >= last_floorclip[dc_x])
-			dc_yh = last_floorclip[dc_x] - 1;
-		if (dc_yl <= last_ceilingclip[dc_x])
-			dc_yl = last_ceilingclip[dc_x] + 1;
-		if (dc_yl <= dc_yh && dl_yh < vid.height && yh > 0)
-		{
-			dc_source = (UINT8 *)column + 3;
-			dc_texturemid = basetexturemid - (topdelta<<FRACBITS);
-
-			// Drawn by R_DrawColumn.
-			colfunc();
-		}
-		column = (column_t *)((UINT8 *)column + column->length + 4);
-	}
-
-	dc_texturemid = basetexturemid;
-}
-
-static void R_DrawWallSplats(void)
-{
-	wallsplat_t *splat;
-	seg_t *seg;
-	angle_t angle, angle1, angle2;
-	INT32 x1, x2;
-	size_t pindex;
-	column_t *col;
-	patch_t *patch;
-	fixed_t texturecolumn;
-
-	splat = (wallsplat_t *)linedef->splats;
-
-	I_Assert(splat != NULL);
-
-	seg = ds_p->curline;
-
-	// draw all splats from the line that touches the range of the seg
-	for (; splat; splat = splat->next)
-	{
-		angle1 = R_PointToAngle(splat->v1.x, splat->v1.y);
-		angle2 = R_PointToAngle(splat->v2.x, splat->v2.y);
-		angle1 = (angle1 - viewangle + ANGLE_90)>>ANGLETOFINESHIFT;
-		angle2 = (angle2 - viewangle + ANGLE_90)>>ANGLETOFINESHIFT;
-		// out of the viewangletox lut
-		/// \todo clip it to the screen
-		if (angle1 > FINEANGLES/2 || angle2 > FINEANGLES/2)
-			continue;
-		x1 = viewangletox[angle1];
-		x2 = viewangletox[angle2];
-
-		if (x1 >= x2)
-			continue; // does not cross a pixel
-
-		// splat is not in this seg range
-		if (x2 < ds_p->x1 || x1 > ds_p->x2)
-			continue;
-
-		if (x1 < ds_p->x1)
-			x1 = ds_p->x1;
-		if (x2 > ds_p->x2)
-			x2 = ds_p->x2;
-		if (x2 <= x1)
-			continue;
-
-		// calculate incremental stepping values for texture edges
-		rw_scalestep = ds_p->scalestep;
-		spryscale = ds_p->scale1 + (x1 - ds_p->x1)*rw_scalestep;
-		mfloorclip = floorclip;
-		mceilingclip = ceilingclip;
-
-		patch = W_CachePatchNum(splat->patch, PU_PATCH);
-
-		dc_texturemid = splat->top + (SHORT(patch->height)<<(FRACBITS-1)) - viewz;
-		if (splat->yoffset)
-			dc_texturemid += *splat->yoffset;
-
-		sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
-
-		// set drawing mode
-		switch (splat->flags & SPLATDRAWMODE_MASK)
-		{
-			case SPLATDRAWMODE_OPAQUE:
-				colfunc = colfuncs[BASEDRAWFUNC];
-				break;
-			case SPLATDRAWMODE_TRANS:
-				if (!cv_translucency.value)
-					colfunc = colfuncs[BASEDRAWFUNC];
-				else
-				{
-					dc_transmap = transtables + ((tr_trans50 - 1)<<FF_TRANSSHIFT);
-					colfunc = colfuncs[COLDRAWFUNC_FUZZY];
-				}
-
-				break;
-			case SPLATDRAWMODE_SHADE:
-				colfunc = colfuncs[COLDRAWFUNC_SHADE];
-				break;
-		}
-
-		dc_texheight = 0;
-
-		// draw the columns
-		for (dc_x = x1; dc_x <= x2; dc_x++, spryscale += rw_scalestep)
-		{
-			pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
-			if (pindex >= MAXLIGHTSCALE)
-				pindex = MAXLIGHTSCALE - 1;
-			dc_colormap = walllights[pindex];
-
-			if (frontsector->extra_colormap)
-				dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
-
-			sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
-			dc_iscale = 0xffffffffu / (unsigned)spryscale;
-
-			// find column of patch, from perspective
-			angle = (rw_centerangle + xtoviewangle[dc_x])>>ANGLETOFINESHIFT;
-				texturecolumn = rw_offset2 - splat->offset
-					- FixedMul(FINETANGENT(angle), rw_distance);
-
-			// FIXME!
-			texturecolumn >>= FRACBITS;
-			if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
-				continue;
-
-			// draw the texture
-			col = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
-			R_DrawSplatColumn(col);
-		}
-	} // next splat
-
-	colfunc = colfuncs[BASEDRAWFUNC];
-}
-
-#endif //WALLSPLATS
-
 // ==========================================================================
 // R_RenderMaskedSegRange
 // ==========================================================================
@@ -2786,20 +2622,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 		}
 	}
 
-#ifdef WALLSPLATS
-	if (linedef->splats && cv_splats.value)
-	{
-		// Isn't a bit wasteful to copy the ENTIRE array for every drawseg?
-		M_Memcpy(last_ceilingclip + ds_p->x1, ceilingclip + ds_p->x1,
-			sizeof (INT16) * (ds_p->x2 - ds_p->x1 + 1));
-		M_Memcpy(last_floorclip + ds_p->x1, floorclip + ds_p->x1,
-			sizeof (INT16) * (ds_p->x2 - ds_p->x1 + 1));
-		R_RenderSegLoop();
-		R_DrawWallSplats();
-	}
-	else
-#endif
-		R_RenderSegLoop();
+	R_RenderSegLoop();
 	colfunc = colfuncs[BASEDRAWFUNC];
 
 	if (portalline) // if curline is a portal, set portalrender for drawseg
diff --git a/src/r_splats.c b/src/r_splats.c
index dfec185a11ef2f4d6fc30ca532bd3de003d61942..0b2826107288dc98bdd3eb7ae4c925c815b5609c 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -8,459 +8,253 @@
 // See the 'LICENSE' file for more details.
 //-----------------------------------------------------------------------------
 /// \file  r_splats.c
-/// \brief floor and wall splats
+/// \brief Floor splats
 
 #include "r_draw.h"
 #include "r_main.h"
-#include "r_plane.h"
 #include "r_splats.h"
+#include "r_bsp.h"
 #include "w_wad.h"
 #include "z_zone.h"
-#include "d_netcmd.h"
 
-#ifdef WALLSPLATS
-static wallsplat_t wallsplats[MAXLEVELSPLATS]; // WALL splats
-static INT32 freewallsplat;
-#endif
-
-#ifdef USEASM
-/// \brief for floorsplats \note accessed by asm code
-struct rastery_s *prastertab;
-#endif
+struct rastery_s *prastertab; // for ASM code
 
 #ifdef FLOORSPLATS
-static floorsplat_t floorsplats[1]; // FLOOR splats
-static INT32 freefloorsplat;
-
-struct rastery_s
-{
-	fixed_t minx, maxx; // for each raster line starting at line 0
-	fixed_t tx1, ty1;
-	fixed_t tx2, ty2; // start/end points in texture at this line
-};
 static struct rastery_s rastertab[MAXVIDHEIGHT];
-
 static void prepare_rastertab(void);
-#endif
-
-// --------------------------------------------------------------------------
-// setup splat cache
-// --------------------------------------------------------------------------
-void R_ClearLevelSplats(void)
-{
-#ifdef WALLSPLATS
-	freewallsplat = 0;
-	memset(wallsplats, 0, sizeof (wallsplats));
-#endif
-#ifdef FLOORSPLATS
-	freefloorsplat = 0;
-	memset(floorsplats, 0, sizeof (floorsplats));
 
-	// setup to draw floorsplats
-	prastertab = rastertab;
-	prepare_rastertab();
-#endif
-}
+UINT8 ds_splatclip[MAXVIDWIDTH];
 
 // ==========================================================================
-//                                                                WALL SPLATS
+//                                                               FLOOR SPLATS
 // ==========================================================================
-#ifdef WALLSPLATS
-// --------------------------------------------------------------------------
-// Return a pointer to a splat free for use, or NULL if no more splats are
-// available
-// --------------------------------------------------------------------------
-static wallsplat_t *R_AllocWallSplat(void)
-{
-	wallsplat_t *splat;
-	wallsplat_t *p_splat;
-	line_t *li;
-
-	// clear the splat from the line if it was in use
-	splat = &wallsplats[freewallsplat];
-	li = splat->line;
-	if (li)
-	{
-		// remove splat from line splats list
-		if (li->splats == splat)
-			li->splats = splat->next; // remove from head
-		else
-		{
-			I_Assert(li->splats != NULL);
-			for (p_splat = li->splats; p_splat->next; p_splat = p_splat->next)
-				if (p_splat->next == splat)
-				{
-					p_splat->next = splat->next;
-					break;
-				}
-		}
-	}
-
-	memset(splat, 0, sizeof (wallsplat_t));
 
-	// for next allocation
-	freewallsplat++;
-	if (freewallsplat >= 20)
-		freewallsplat = 0;
-
-	return splat;
-}
+#ifdef USEASM
+void ASMCALL rasterize_segment_tex_asm(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2, INT32 tc, INT32 dir);
+#endif
 
-// Add a new splat to the linedef:
-// top: top z coord
-// wallfrac: frac along the linedef vector (0 to FRACUNIT)
-// splatpatchname: name of patch to draw
-void R_AddWallSplat(line_t *wallline, INT16 sectorside, const char *patchname, fixed_t top,
-	fixed_t wallfrac, INT32 flags)
+// Lactozilla
+static void rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2, INT32 tc, INT32 dir)
 {
-	fixed_t fracsplat, linelength;
-	wallsplat_t *splat = NULL;
-	wallsplat_t *p_splat;
-	patch_t *patch;
-	sector_t *backsector = NULL;
-
-	if (W_CheckNumForName(patchname) != LUMPERROR)
-		splat = R_AllocWallSplat();
-	if (!splat)
+#ifdef USEASM
+	if (R_ASM)
+	{
+		rasterize_segment_tex_asm(x1, y1, x2, y2, tv1, tv2, tc, dir);
 		return;
-
-	// set the splat
-	splat->patch = W_GetNumForName(patchname);
-	sectorside ^= 1;
-	if (wallline->sidenum[sectorside] != 0xffff)
+	}
+	else
+#endif
 	{
-		backsector = sides[wallline->sidenum[sectorside]].sector;
+		fixed_t xs, xe, count;
+		fixed_t dx0, dx1;
 
-		if (top < backsector->floorheight)
-		{
-			splat->yoffset = &backsector->floorheight;
-			top -= backsector->floorheight;
-		}
-		else if (top > backsector->ceilingheight)
-		{
-			splat->yoffset = &backsector->ceilingheight;
-			top -= backsector->ceilingheight;
-		}
-	}
+		if (y1 == y2)
+			return;
 
-	splat->top = top;
-	splat->flags = flags;
+		if (y2 > y1)
+		{
+			count = (y2-y1)+1;
 
-	// bad.. but will be needed for drawing anyway..
-	patch = W_CachePatchNum(splat->patch, PU_PATCH);
+			dx0 = FixedDiv((x2-x1)<<FRACBITS, count<<FRACBITS);
+			dx1 = FixedDiv((tv2-tv1)<<FRACBITS, count<<FRACBITS);
 
-	// offset needed by draw code for texture mapping
-	linelength = P_SegLength((seg_t *)wallline);
-	splat->offset = FixedMul(wallfrac, linelength) - (SHORT(patch->width)<<(FRACBITS-1));
-	fracsplat = FixedDiv(((SHORT(patch->width)<<FRACBITS)>>1), linelength);
+			xs = x1 << FRACBITS;
+			xe = tv1 << FRACBITS;
+			tc <<= FRACBITS;
 
-	wallfrac -= fracsplat;
-	if (wallfrac > linelength)
-		return;
-	splat->v1.x = wallline->v1->x + FixedMul(wallline->dx, wallfrac);
-	splat->v1.y = wallline->v1->y + FixedMul(wallline->dy, wallfrac);
-	wallfrac += fracsplat + fracsplat;
-	if (wallfrac < 0)
-		return;
-	splat->v2.x = wallline->v1->x + FixedMul(wallline->dx, wallfrac);
-	splat->v2.y = wallline->v1->y + FixedMul(wallline->dy, wallfrac);
+			if (dir == 0)
+			{
+				for (;;)
+				{
+					rastertab[y1].maxx = xs;
+					rastertab[y1].tx2 = xe;
+					rastertab[y1].ty2 = tc;
 
-	if (wallline->frontsector && wallline->frontsector == backsector)
-		return;
+					xs += dx0;
+					xe += dx1;
+					y1++;
 
-	// insert splat in the linedef splat list
-	// BP: why not insert in head is much more simple?
-	// BP: because for remove it is more simple!
-	splat->line = wallline;
-	splat->next = NULL;
-	if (wallline->splats)
-	{
-		p_splat = wallline->splats;
-		while (p_splat->next)
-			p_splat = p_splat->next;
-		p_splat->next = splat;
-	}
-	else
-		wallline->splats = splat;
-}
-#endif // WALLSPLATS
+					if (count-- < 1) break;
+				}
+			}
+			else
+			{
+				for (;;)
+				{
+					rastertab[y1].maxx = xs;
+					rastertab[y1].tx2 = tc;
+					rastertab[y1].ty2 = xe;
 
-// ==========================================================================
-//                                                               FLOOR SPLATS
-// ==========================================================================
-#ifdef FLOORSPLATS
+					xs += dx0;
+					xe += dx1;
+					y1++;
 
-// --------------------------------------------------------------------------
-// Return a pointer to a splat free for use, or NULL if no more splats are
-// available
-// --------------------------------------------------------------------------
-static floorsplat_t *R_AllocFloorSplat(void)
-{
-	floorsplat_t *splat;
-	floorsplat_t *p_splat;
-	subsector_t *sub;
-
-	// find splat to use
-	freefloorsplat++;
-	if (freefloorsplat >= 1)
-		freefloorsplat = 0;
-
-	// clear the splat from the line if it was in use
-	splat = &floorsplats[freefloorsplat];
-	sub = splat->subsector;
-	if (sub)
-	{
-		// remove splat from subsector splats list
-		if (sub->splats == splat)
-			sub->splats = splat->next; // remove from head
-		else
-		{
-			p_splat = sub->splats;
-			while (p_splat->next)
-			{
-				if (p_splat->next == splat)
-					p_splat->next = splat->next;
+					if (count-- < 1) break;
+				}
 			}
 		}
-	}
-
-	memset(splat, 0, sizeof (floorsplat_t));
-	return splat;
-}
-
-// --------------------------------------------------------------------------
-// Add a floor splat to the subsector
-// --------------------------------------------------------------------------
-void R_AddFloorSplat(subsector_t *subsec, mobj_t *mobj, const char *picname, fixed_t x, fixed_t y, fixed_t z,
-	INT32 flags)
-{
-	floorsplat_t *splat = NULL;
-	floorsplat_t *p_splat;
-	INT32 size;
-
-	if (W_CheckNumForName(picname) != LUMPERROR)
-		splat = R_AllocFloorSplat();
-	if (!splat)
-		return;
-
-	// set the splat
-	splat->pic = W_GetNumForName(picname);
-	splat->flags = flags;
-	splat->mobj = mobj;
+		else
+		{
+			count = (y1-y2)+1;
 
-	splat->z = z;
+			dx0 = FixedDiv((x1-x2)<<FRACBITS, count<<FRACBITS);
+			dx1 = FixedDiv((tv1-tv2)<<FRACBITS, count<<FRACBITS);
 
-	size = W_LumpLength(splat->pic);
+			xs = x2 << FRACBITS;
+			xe = tv2 << FRACBITS;
+			tc <<= FRACBITS;
 
-	switch (size)
-	{
-		case 4194304: // 2048x2048 lump
-			splat->size = 1024;
-			break;
-		case 1048576: // 1024x1024 lump
-			splat->size = 512;
-			break;
-		case 262144:// 512x512 lump
-			splat->size = 256;
-			break;
-		case 65536: // 256x256 lump
-			splat->size = 128;
-			break;
-		case 16384: // 128x128 lump
-			splat->size = 64;
-			break;
-		case 1024: // 32x32 lump
-			splat->size = 16;
-			break;
-		default: // 64x64 lump
-			splat->size = 32;
-			break;
-	}
+			if (dir == 0)
+			{
+				for (;;)
+				{
+					rastertab[y2].minx = xs;
+					rastertab[y2].tx1 = xe;
+					rastertab[y2].ty1 = tc;
 
-	// 3--2
-	// |  |
-	// 0--1
-	//
-	splat->verts[0].x = splat->verts[3].x = x - (splat->size<<FRACBITS);
-	splat->verts[2].x = splat->verts[1].x = x + ((splat->size-1)<<FRACBITS);
-	splat->verts[3].y = splat->verts[2].y = y + ((splat->size-1)<<FRACBITS);
-	splat->verts[0].y = splat->verts[1].y = y - (splat->size<<FRACBITS);
-
-	// insert splat in the subsector splat list
-	splat->subsector = subsec;
-	splat->next = NULL;
-	if (subsec->splats)
-	{
-		p_splat = subsec->splats;
-		while (p_splat->next)
-			p_splat = p_splat->next;
-		p_splat->next = splat;
-	}
-	else
-		subsec->splats = splat;
-}
+					xs += dx0;
+					xe += dx1;
+					y2++;
 
-// --------------------------------------------------------------------------
-// Before each frame being rendered, clear the visible floorsplats list
-// --------------------------------------------------------------------------
-static floorsplat_t *visfloorsplats;
-
-void R_ClearVisibleFloorSplats(void)
-{
-	visfloorsplats = NULL;
-}
+					if (count-- < 1) break;
+				}
+			}
+			else
+			{
+				for (;;)
+				{
+					rastertab[y2].minx = xs;
+					rastertab[y2].tx1 = tc;
+					rastertab[y2].ty1 = xe;
 
-// --------------------------------------------------------------------------
-// Add a floorsplat to the visible floorsplats list, for the current frame
-// --------------------------------------------------------------------------
-void R_AddVisibleFloorSplats(subsector_t *subsec)
-{
-	floorsplat_t *pSplat;
-	I_Assert(subsec->splats != NULL);
-
-	pSplat = subsec->splats;
-	// the splat is not visible from below
-	// FIXME: depending on some flag in pSplat->flags, some splats may be visible from 2 sides
-	// (above/below)
-	if (pSplat->z < viewz)
-	{
-		pSplat->nextvis = visfloorsplats;
-		visfloorsplats = pSplat;
-	}
+					xs += dx0;
+					xe += dx1;
+					y2++;
 
-	while (pSplat->next)
-	{
-		pSplat = pSplat->next;
-		if (pSplat->z < viewz)
-		{
-			pSplat->nextvis = visfloorsplats;
-			visfloorsplats = pSplat;
+					if (count-- < 1) break;
+				}
+			}
 		}
 	}
 }
 
-#ifdef USEASM
-// tv1, tv2 = x/y qui varie dans la texture, tc = x/y qui est constant.
-void ASMCALL rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2,
-	INT32 tc, INT32 dir);
-#endif
-
-// current test with floor tile
-//#define FLOORSPLATSOLIDCOLOR
-
 // --------------------------------------------------------------------------
 // Rasterize the four edges of a floor splat polygon,
 // fill the polygon with linear interpolation, call span drawer for each
 // scan line
 // --------------------------------------------------------------------------
-static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTex)
+void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, vissprite_t *vis)
 {
 	// rasterizing
-	INT32 miny = vid.height + 1, maxy = 0, y, x1, ry1, x2, y2;
-	fixed_t offsetx, offsety;
-
-#ifdef FLOORSPLATSOLIDCOLOR
-	UINT8 *pDest;
-	INT32 tdx, tdy, ty, tx, x;
-#else
-	lighttable_t **planezlight;
+	INT32 miny = viewheight + 1, maxy = 0, y, x1, ry1, x2, y2, i;
+	fixed_t offsetx = 0, offsety = 0;
+	fixed_t step;
+
 	fixed_t planeheight;
+	fixed_t xstep, ystep;
 	angle_t angle, planecos, planesin;
 	fixed_t distance, span;
-	size_t indexr;
-	INT32 light;
-#endif
-	(void)pTex;
 
-	offsetx = pSplat->verts[0].x & ((pSplat->size << FRACBITS)-1);
-	offsety = pSplat->verts[0].y & ((pSplat->size << FRACBITS)-1);
+	int spanfunctype = SPANDRAWFUNC_SPRITE;
 
-	// do segment a -> top of texture
-	x1 = verts[3].x;
-	ry1 = verts[3].y;
-	x2 = verts[2].x;
-	y2 = verts[2].y;
-	if (ry1 < 0)
-		ry1 = 0;
-	if (ry1 >= vid.height)
-		ry1 = vid.height - 1;
-	if (y2 < 0)
-		y2 = 0;
-	if (y2 >= vid.height)
-		y2 = vid.height - 1;
-	rasterize_segment_tex(x1, ry1, x2, y2, 0, pSplat->size - 1, 0, 0);
-	if (ry1 < miny)
-		miny = ry1;
-	if (ry1 > maxy)
-		maxy = ry1;
+	prepare_rastertab();
 
-	// do segment b -> right side of texture
-	x1 = x2;
-	ry1 = y2;
-	x2 = verts[1].x;
-	y2 = verts[1].y;
-	if (ry1 < 0)
-		ry1 = 0;
-	if (ry1 >= vid.height)
-		ry1 = vid.height - 1;
-	if (y2 < 0)
-		y2 = 0;
-	if (y2 >= vid.height)
-		y2 = vid.height - 1;
-	rasterize_segment_tex(x1, ry1, x2, y2, 0, pSplat->size - 1, pSplat->size - 1, 1);
-	if (ry1 < miny)
-		miny = ry1;
-	if (ry1 > maxy)
-		maxy = ry1;
+#define RASTERPARAMS(vnum1, vnum2, tv1, tv2, tc, dir) \
+    x1 = verts[vnum1].x; \
+    ry1 = verts[vnum1].y; \
+    x2 = verts[vnum2].x; \
+    y2 = verts[vnum2].y; \
+    if (y2 > ry1) \
+        step = FixedDiv(x2-x1, y2-ry1+1); \
+    else if (y2 == ry1) \
+        step = 0; \
+    else \
+        step = FixedDiv(x2-x1, ry1-y2+1); \
+    if (ry1 < 0) { \
+        if (step) { \
+            x1 <<= FRACBITS; \
+            x1 += (-ry1)*step; \
+            x1 >>= FRACBITS; \
+        } \
+        ry1 = 0; \
+    } \
+    if (ry1 >= vid.height) { \
+        if (step) { \
+            x1 <<= FRACBITS; \
+            x1 -= (vid.height-1-ry1)*step; \
+            x1 >>= FRACBITS; \
+        } \
+        ry1 = vid.height - 1; \
+    } \
+    if (y2 < 0) { \
+        if (step) { \
+            x2 <<= FRACBITS; \
+            x2 -= (-y2)*step; \
+            x2 >>= FRACBITS; \
+        } \
+        y2 = 0; \
+    } \
+    if (y2 >= vid.height) { \
+        if (step) { \
+            x2 <<= FRACBITS; \
+            x2 += (vid.height-1-y2)*step; \
+            x2 >>= FRACBITS; \
+        } \
+        y2 = vid.height - 1; \
+    } \
+    rasterize_segment_tex(x1, ry1, x2, y2, tv1, tv2, tc, dir); \
+    if (ry1 < miny) \
+        miny = ry1; \
+    if (ry1 > maxy) \
+        maxy = ry1;
 
+	// do segment a -> top of texture
+	RASTERPARAMS(3,2,0,pSplat->width-1,0,0);
+	// do segment b -> right side of texture
+	RASTERPARAMS(2,1,0,pSplat->width-1,pSplat->height-1,0);
 	// do segment c -> bottom of texture
-	x1 = x2;
-	ry1 = y2;
-	x2 = verts[0].x;
-	y2 = verts[0].y;
-	if (ry1 < 0)
-		ry1 = 0;
-	if (ry1 >= vid.height)
-		ry1 = vid.height - 1;
-	if (y2 < 0)
-		y2 = 0;
-	if (y2 >= vid.height)
-		y2 = vid.height - 1;
-	rasterize_segment_tex(x1, ry1, x2, y2, pSplat->size - 1, 0, pSplat->size - 1, 0);
-	if (ry1 < miny)
-		miny = ry1;
-	if (ry1 > maxy)
-		maxy = ry1;
-
+	RASTERPARAMS(1,0,pSplat->width-1,0,pSplat->height-1,0);
 	// do segment d -> left side of texture
-	x1 = x2;
-	ry1 = y2;
-	x2 = verts[3].x;
-	y2 = verts[3].y;
-	if (ry1 < 0)
-		ry1 = 0;
-	if (ry1 >= vid.height)
-		ry1 = vid.height - 1;
-	if (y2 < 0)
-		y2 = 0;
-	if (y2 >= vid.height)
-		y2 = vid.height - 1;
-	rasterize_segment_tex(x1, ry1, x2, y2, pSplat->size - 1, 0, 0, 1);
-	if (ry1 < miny)
-		miny = ry1;
-	if (ry1 > maxy)
-		maxy = ry1;
-
-#ifndef FLOORSPLATSOLIDCOLOR
-	// prepare values for all the splat
-	ds_source = W_CacheLumpNum(pSplat->pic, PU_CACHE);
+	RASTERPARAMS(0,3,pSplat->width-1,0,0,1);
+
+	ds_source = pSplat->pic;
+	ds_flatwidth = pSplat->width;
+	ds_flatheight = pSplat->height;
+
+	if (R_CheckPowersOfTwo())
+		R_CheckFlatLength(ds_flatwidth * ds_flatheight);
+
+	ds_transmap = NULL;
+
+	if (vis->transmap)
+	{
+		ds_transmap = vis->transmap;
+		spanfunctype = SPANDRAWFUNC_TRANSSPRITE;
+	}
+
+	if (ds_powersoftwo)
+		spanfunc = spanfuncs[spanfunctype];
+	else
+		spanfunc = spanfuncs_npo2[spanfunctype];
+
+	if (pSplat->angle)
+	{
+		memset(cachedheight, 0, sizeof(cachedheight));
+		angle = (viewangle + pSplat->angle - ANGLE_90) >> ANGLETOFINESHIFT;
+		basexscale = FixedDiv(FINECOSINE(angle), centerxfrac);
+		baseyscale = -FixedDiv(FINESINE(angle), centerxfrac);
+	}
+	else
+	{
+		angle = (viewangle - ANGLE_90) >> ANGLETOFINESHIFT;
+		basexscale = FixedDiv(FINECOSINE(angle), centerxfrac);
+		baseyscale = -FixedDiv(FINESINE(angle), centerxfrac);
+	}
+
 	planeheight = abs(pSplat->z - viewz);
-	light = (pSplat->subsector->sector->lightlevel >> LIGHTSEGSHIFT);
-	if (light >= LIGHTLEVELS)
-		light = LIGHTLEVELS - 1;
-	if (light < 0)
-		light = 0;
-	planezlight = zlight[light];
+
+	if (maxy >= vid.height)
+		maxy = vid.height-1;
 
 	for (y = miny; y <= maxy; y++)
 	{
@@ -472,7 +266,7 @@ static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTe
 		if (x2 >= vid.width)
 			x2 = vid.width - 1;
 
-		angle = (currentplane->viewangle + currentplane->plangle)>>ANGLETOFINESHIFT;
+		angle = (viewangle + pSplat->angle)>>ANGLETOFINESHIFT;
 		planecos = FINECOSINE(angle);
 		planesin = FINESINE(angle);
 
@@ -480,146 +274,115 @@ static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTe
 		{
 			cachedheight[y] = planeheight;
 			distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]);
-			ds_xstep = cachedxstep[y] = FixedMul(distance,basexscale);
-			ds_ystep = cachedystep[y] = FixedMul(distance,baseyscale);
-
+			xstep = cachedxstep[y] = FixedMul(distance, basexscale);
+			ystep = cachedystep[y] = FixedMul(distance, baseyscale);
+			// don't divide by zero
 			if ((span = abs(centery-y)))
 			{
-				ds_xstep = cachedxstep[y] = FixedMul(planesin, planeheight) / span;
-				ds_ystep = cachedystep[y] = FixedMul(planecos, planeheight) / span;
+				xstep = cachedxstep[y] = FixedMul(planesin, planeheight) / span;
+				ystep = cachedystep[y] = FixedMul(planecos, planeheight) / span;
 			}
 		}
 		else
 		{
 			distance = cacheddistance[y];
-			ds_xstep = cachedxstep[y];
-			ds_ystep = cachedystep[y];
+			xstep = cachedxstep[y];
+			ystep = cachedystep[y];
 		}
 
-		ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep;
-		ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep;
-		ds_xfrac -= offsetx;
-		ds_yfrac += offsety;
+		ds_xstep = FixedDiv(xstep, pSplat->xscale);
+		ds_ystep = FixedDiv(ystep, pSplat->yscale);
 
-		indexr = distance >> LIGHTZSHIFT;
-		if (indexr >= MAXLIGHTZ)
-			indexr = MAXLIGHTZ - 1;
-		ds_colormap = planezlight[indexr];
+		ds_colormap = vis->colormap;
+		ds_translation = R_GetSpriteTranslation(vis);
+		if (ds_translation == NULL)
+			ds_translation = colormaps;
 
-		ds_y = y;
-		if (x2 >= x1) // sanity check
+		if (vis->extra_colormap)
 		{
-			ds_x1 = x1;
-			ds_x2 = x2;
-			ds_transmap = transtables + ((tr_trans50-1)<<FF_TRANSSHIFT);
-			(spanfuncs[SPANDRAWFUNC_SPLAT])();
+			if (!ds_colormap)
+				ds_colormap = vis->extra_colormap->colormap;
+			else
+				ds_colormap = &vis->extra_colormap->colormap[ds_colormap - colormaps];
 		}
 
-		// reset for next calls to edge rasterizer
-		rastertab[y].minx = INT32_MAX;
-		rastertab[y].maxx = INT32_MIN;
-	}
-
-#else
-	for (y = miny; y <= maxy; y++)
-	{
-		x1 = rastertab[y].minx>>FRACBITS;
-		x2 = rastertab[y].maxx>>FRACBITS;
-		if (x1 < 0)
-			x1 = 0;
-		if (x2 >= vid.width)
-			x2 = vid.width - 1;
+		if (pSplat->angle)
+		{
+			// Add the view offset, rotated by the plane angle.
+			fixed_t a = -pSplat->verts[0].x + viewx;
+			fixed_t b = -pSplat->verts[0].y + viewy;
+			angle = (pSplat->angle >> ANGLETOFINESHIFT);
+			offsetx = FixedMul(a, FINECOSINE(angle)) - FixedMul(b,FINESINE(angle));
+			offsety = -FixedMul(a, FINESINE(angle)) - FixedMul(b,FINECOSINE(angle));
+		}
+		else
+		{
+			offsetx = viewx - pSplat->verts[0].x;
+			offsety = pSplat->verts[0].y - viewy;
+		}
 
-//		pDest = ylookup[y] + columnofs[x1];
-		pDest = &topleft[y*vid.width + x1];
+		if (vis != NULL)
+		{
+			INT32 xclip;
 
-		x = x2 - x1 + 1;
+			mfloorclip = vis->clipbot;
+			mceilingclip = vis->cliptop;
 
-		// starting point of the texture
-		tx = rastertab[y].tx1;
-		ty = rastertab[y].ty1;
+			R_ClipVisSprite(vis, x1-1, x2+1, drawsegs, NULL);
+			memset(ds_splatclip, 0, sizeof(ds_splatclip));
 
-		// HORRIBLE BUG!!!
-		if (x > 0)
-		{
-			tdx = (rastertab[y].tx2 - tx) / x;
-			tdy = (rastertab[y].ty2 - ty) / x;
+			if (x2 >= x1 && x1 < viewwidth && x1 >= 0)
+			{
+				for (xclip = x1; xclip <= x2; xclip++)
+				{
+					if (y >= mfloorclip[xclip])
+						ds_splatclip[xclip] = 1;
+				}
+			}
 
-			while (x-- > 0)
+			while (ds_splatclip[x1])
+				x1++;
+			i = x2;
+			while (i > x1)
 			{
-				*(pDest++) = (UINT8)(y&1);
-				tx += tdx;
-				ty += tdy;
+				if (ds_splatclip[i])
+					x2 = i-1;
+				i--;
 			}
 		}
 
-		// reinitialise the minimum and maximum for the next approach
+		ds_xfrac = FixedDiv(offsetx + FixedMul(planecos, distance) + (x1 - centerx) * xstep, pSplat->xscale);
+		ds_yfrac = FixedDiv(offsety - FixedMul(planesin, distance) + (x1 - centerx) * ystep, pSplat->yscale);
+
+		if (x2 >= x1)
+		{
+			ds_y = y;
+			ds_x1 = x1;
+			ds_x2 = x2;
+			spanfunc();
+		}
+
 		rastertab[y].minx = INT32_MAX;
 		rastertab[y].maxx = INT32_MIN;
 	}
-#endif
-}
 
-// --------------------------------------------------------------------------
-// R_DrawVisibleFloorSplats
-// draw the flat floor/ceiling splats
-// --------------------------------------------------------------------------
-void R_DrawVisibleFloorSplats(void)
-{
-	floorsplat_t *pSplat;
-	INT32 iCount = 0, i;
-	fixed_t tr_x, tr_y, rot_x, rot_y, rot_z, xscale, yscale;
-	vertex_t *v3d;
-	vertex_t v2d[4];
-
-	pSplat = visfloorsplats;
-	while (pSplat)
+	if (pSplat->angle)
 	{
-		iCount++;
-
-		// Draw a floor splat
-		// 3--2
-		// |  |
-		// 0--1
-
-		rot_z = pSplat->z - viewz;
-		for (i = 0; i < 4; i++)
-		{
-			v3d = &pSplat->verts[i];
-
-			// transform the origin point
-			tr_x = v3d->x - viewx;
-			tr_y = v3d->y - viewy;
-
-			// rotation around vertical y axis
-			rot_x = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
-			rot_y = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
-
-			if (rot_y < 4*FRACUNIT)
-				goto skipit;
-
-			// note: y from view above of map, is distance far away
-			xscale = FixedDiv(projection, rot_y);
-			yscale = -FixedDiv(projectiony, rot_y);
-
-			// projection
-			v2d[i].x = (centerxfrac + FixedMul (rot_x, xscale))>>FRACBITS;
-			v2d[i].y = (centeryfrac + FixedMul (rot_z, yscale))>>FRACBITS;
-		}
-
-		R_RenderFloorSplat(pSplat, v2d, NULL);
-skipit:
-		pSplat = pSplat->nextvis;
+		memset(cachedheight, 0, sizeof(cachedheight));
+		angle = (viewangle - ANGLE_90) >> ANGLETOFINESHIFT;
+		basexscale = FixedDiv(FINECOSINE(angle), centerxfrac);
+		baseyscale = -FixedDiv(FINESINE(angle), centerxfrac);
 	}
 }
 
 static void prepare_rastertab(void)
 {
-	INT32 iLine;
-	for (iLine = 0; iLine < vid.height; iLine++)
+	INT32 i;
+	prastertab = rastertab;
+	for (i = 0; i < vid.height; i++)
 	{
-		rastertab[iLine].minx = INT32_MAX;
-		rastertab[iLine].maxx = INT32_MIN;
+		rastertab[i].minx = INT32_MAX;
+		rastertab[i].maxx = INT32_MIN;
 	}
 }
 
diff --git a/src/r_splats.h b/src/r_splats.h
index 4ad893abbb2db5dcde78116fd94b65cf8f373aa4..9c01084cf1750b4fea0141f9837156f20b4cedac 100644
--- a/src/r_splats.h
+++ b/src/r_splats.h
@@ -14,68 +14,35 @@
 #define __R_SPLATS_H__
 
 #include "r_defs.h"
-
-//#define WALLSPLATS      // comment this out to compile without splat effects
-/*#ifdef USEASM
-#define FLOORSPLATS
-#endif*/
-
-#define MAXLEVELSPLATS      1024
-
-// splat flags
-#define SPLATDRAWMODE_MASK 0x03 // mask to get drawmode from flags
-#define SPLATDRAWMODE_OPAQUE 0x00
-#define SPLATDRAWMODE_SHADE 0x01
-#define SPLATDRAWMODE_TRANS 0x02
+#include "r_things.h"
 
 // ==========================================================================
 // DEFINITIONS
 // ==========================================================================
 
-// WALL SPLATS are patches drawn on top of wall segs
-typedef struct wallsplat_s
+struct rastery_s
 {
-	lumpnum_t patch; // lump id.
-	vertex_t v1, v2; // vertices along the linedef
-	fixed_t top;
-	fixed_t offset; // offset in columns<<FRACBITS from start of linedef to start of splat
-	INT32 flags;
-	fixed_t *yoffset;
-	line_t *line; // the parent line of the splat seg
-	struct wallsplat_s *next;
-} wallsplat_t;
+	fixed_t minx, maxx; // for each raster line starting at line 0
+	fixed_t tx1, ty1;   // start points in texture at this line
+	fixed_t tx2, ty2;   // end points in texture at this line
+};
+extern struct rastery_s *prastertab; // for ASM code
 
-// FLOOR SPLATS are pic_t (raw horizontally stored) drawn on top of the floor or ceiling
+#ifdef FLOORSPLATS
 typedef struct floorsplat_s
 {
-	lumpnum_t pic; // a pic_t lump id
-	INT32 flags;
-	INT32 size; // 64, 128, 256, etc.
-	vertex_t verts[4]; // (x,y) as viewn from above on map
-	fixed_t z; // z (height) is constant for all the floorsplats
-	subsector_t *subsector; // the parent subsector
+	UINT8 *pic;
+	INT32 width, height;
+	fixed_t scale, xscale, yscale;
+	angle_t angle;
+
+	vertex_t verts[4]; // (x,y) as viewed from above on map
+	fixed_t x, y, z; // position
 	mobj_t *mobj; // Mobj it is tied to
-	struct floorsplat_s *next;
-	struct floorsplat_s *nextvis;
 } floorsplat_t;
 
-// p_setup.c
-fixed_t P_SegLength(seg_t *seg);
-
-// call at P_SetupLevel()
-void R_ClearLevelSplats(void);
-
-#ifdef WALLSPLATS
-void R_AddWallSplat(line_t *wallline, INT16 sectorside, const char *patchname, fixed_t top,
-	fixed_t wallfrac, INT32 flags);
+void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, vissprite_t *vis);
+extern UINT8 ds_splatclip[MAXVIDWIDTH];
 #endif
-#ifdef FLOORSPLATS
-void R_AddFloorSplat(subsector_t *subsec, mobj_t *mobj, const char *picname, fixed_t x, fixed_t y, fixed_t z,
-	INT32 flags);
-#endif
-
-void R_ClearVisibleFloorSplats(void);
-void R_AddVisibleFloorSplats(subsector_t *subsec);
-void R_DrawVisibleFloorSplats(void);
 
 #endif /*__R_SPLATS_H__*/
diff --git a/src/r_things.c b/src/r_things.c
index 33b9820c81d18a53f8605935f293eda2ee5ac026..ac9c357a44710825d6c0ba278274a62799509ec1 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -29,6 +29,7 @@
 #include "r_picformats.h"
 #include "r_plane.h"
 #include "r_portal.h"
+#include "r_splats.h"
 #include "p_tick.h"
 #include "p_local.h"
 #include "p_slopes.h"
@@ -108,7 +109,10 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 
 #ifdef ROTSPRITE
 	for (r = 0; r < 16; r++)
-		sprtemp[frame].rotated[r] = NULL;
+	{
+		sprtemp[frame].rotated[0][r] = NULL;
+		sprtemp[frame].rotated[1][r] = NULL;
+	}
 #endif
 
 	if (rotation == 0)
@@ -737,6 +741,77 @@ void R_DrawFlippedMaskedColumn(column_t *column)
 	dc_texturemid = basetexturemid;
 }
 
+boolean R_SpriteIsFlashing(vissprite_t *vis)
+{
+	return (!(vis->cut & SC_PRECIP)
+	&& (vis->mobj->flags & (MF_ENEMY|MF_BOSS))
+	&& (vis->mobj->flags2 & MF2_FRET)
+	&& !(vis->mobj->flags & MF_GRENADEBOUNCE)
+	&& (leveltime & 1));
+}
+
+UINT8 *R_GetSpriteTranslation(vissprite_t *vis)
+{
+	if (R_SpriteIsFlashing(vis)) // Bosses "flash"
+	{
+		if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
+			return R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
+		else if (vis->mobj->type == MT_METALSONIC_BATTLE)
+			return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
+		else
+			return R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE);
+	}
+	else if (vis->mobj->color && vis->transmap) // Color mapping
+	{
+		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
+			return R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
+		else if (!(vis->cut & SC_PRECIP)
+			&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
+			&& (vis->mobj->player->charflags & SF_DASHMODE)
+			&& ((leveltime/2) & 1))
+		{
+			if (vis->mobj->player->charflags & SF_MACHINE)
+				return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
+			else
+				return R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
+		}
+		else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // MT_GHOST LOOKS LIKE A PLAYER SO USE THE PLAYER TRANSLATION TABLES. >_>
+		{
+			size_t skinnum = (skin_t*)vis->mobj->skin-skins;
+			return R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
+		}
+		else // Use the defaults
+			return R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color, GTC_CACHE);
+	}
+	else if (vis->mobj->color)
+	{
+		// New colormap stuff for skins Tails 06-07-2002
+		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
+			return R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
+		else if (!(vis->cut & SC_PRECIP)
+			&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
+			&& (vis->mobj->player->charflags & SF_DASHMODE)
+			&& ((leveltime/2) & 1))
+		{
+			if (vis->mobj->player->charflags & SF_MACHINE)
+				return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
+			else
+				return R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
+		}
+		else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player!
+		{
+			size_t skinnum = (skin_t*)vis->mobj->skin-skins;
+			return R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
+		}
+		else // Use the defaults
+			return R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color, GTC_CACHE);
+	}
+	else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
+		return R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);
+
+	return NULL;
+}
+
 //
 // R_DrawVisSprite
 //  mfloorclip and mceilingclip should also be set.
@@ -770,77 +845,24 @@ static void R_DrawVisSprite(vissprite_t *vis)
 
 	colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
 	dc_colormap = vis->colormap;
-	if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && !(vis->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash"
-	{
-		// translate certain pixels to white
-		colfunc = colfuncs[COLDRAWFUNC_TRANS];
-		if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
-			dc_translation = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
-		else if (vis->mobj->type == MT_METALSONIC_BATTLE)
-			dc_translation = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
-		else
-			dc_translation = R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE);
-	}
+	dc_translation = R_GetSpriteTranslation(vis);
+
+	if (R_SpriteIsFlashing(vis)) // Bosses "flash"
+		colfunc = colfuncs[COLDRAWFUNC_TRANS]; // translate certain pixels to white
 	else if (vis->mobj->color && vis->transmap) // Color mapping
 	{
 		colfunc = colfuncs[COLDRAWFUNC_TRANSTRANS];
 		dc_transmap = vis->transmap;
-		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
-			dc_translation = R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
-		else if (!(vis->cut & SC_PRECIP)
-			&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
-			&& (vis->mobj->player->charflags & SF_DASHMODE)
-			&& ((leveltime/2) & 1))
-		{
-			if (vis->mobj->player->charflags & SF_MACHINE)
-				dc_translation = R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
-			else
-				dc_translation = R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
-		}
-		else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // MT_GHOST LOOKS LIKE A PLAYER SO USE THE PLAYER TRANSLATION TABLES. >_>
-		{
-			size_t skinnum = (skin_t*)vis->mobj->skin-skins;
-			dc_translation = R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
-		}
-		else // Use the defaults
-			dc_translation = R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color, GTC_CACHE);
 	}
 	else if (vis->transmap)
 	{
 		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 		dc_transmap = vis->transmap;    //Fab : 29-04-98: translucency table
 	}
-	else if (vis->mobj->color)
-	{
-		// translate green skin to another color
+	else if (vis->mobj->color) // translate green skin to another color
 		colfunc = colfuncs[COLDRAWFUNC_TRANS];
-
-		// New colormap stuff for skins Tails 06-07-2002
-		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
-			dc_translation = R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
-		else if (!(vis->cut & SC_PRECIP)
-			&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
-			&& (vis->mobj->player->charflags & SF_DASHMODE)
-			&& ((leveltime/2) & 1))
-		{
-			if (vis->mobj->player->charflags & SF_MACHINE)
-				dc_translation = R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
-			else
-				dc_translation = R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
-		}
-		else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player!
-		{
-			size_t skinnum = (skin_t*)vis->mobj->skin-skins;
-			dc_translation = R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
-		}
-		else // Use the defaults
-			dc_translation = R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color, GTC_CACHE);
-	}
 	else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
-	{
 		colfunc = colfuncs[COLDRAWFUNC_TRANS];
-		dc_translation = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);
-	}
 
 	if (vis->extra_colormap)
 	{
@@ -923,6 +945,28 @@ static void R_DrawVisSprite(vissprite_t *vis)
 			localcolfunc (column);
 		}
 	}
+	else if (vis->cut & SC_SHEAR)
+	{
+#ifdef RANGECHECK
+		pwidth = SHORT(patch->width);
+#endif
+
+		// Vertically sheared sprite
+		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, dc_texturemid -= vis->shear.tan)
+		{
+#ifdef RANGECHECK
+			texturecolumn = frac>>FRACBITS;
+			if (texturecolumn < 0 || texturecolumn >= pwidth)
+				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
+			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
+#else
+			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
+#endif
+
+			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
+			localcolfunc (column);
+		}
+	}
 	else
 	{
 #ifdef RANGECHECK
@@ -1217,6 +1261,29 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 #undef CHECKZ
 }
 
+static void R_SkewShadowSprite(
+			mobj_t *thing, pslope_t *groundslope,
+			fixed_t groundz, INT32 spriteheight, fixed_t scalemul,
+			fixed_t *shadowyscale, fixed_t *shadowskew)
+{
+	// haha let's try some dumb stuff
+	fixed_t xslope, zslope;
+	angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - groundslope->xydirection) >> ANGLETOFINESHIFT;
+
+	xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta);
+	zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta);
+
+	//CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope);
+
+	if (viewz < groundz)
+		*shadowyscale += FixedMul(FixedMul(thing->radius*2 / spriteheight, scalemul), zslope);
+	else
+		*shadowyscale -= FixedMul(FixedMul(thing->radius*2 / spriteheight, scalemul), zslope);
+
+	*shadowyscale = abs((*shadowyscale));
+	*shadowskew = xslope;
+}
+
 static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz)
 {
 	vissprite_t *shadow;
@@ -1245,40 +1312,22 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	yscale = FixedDiv(projectiony, tz);
 	shadowxscale = FixedMul(thing->radius*2, scalemul);
 	shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz));
-	shadowyscale = min(shadowyscale, shadowxscale) / SHORT(patch->height);
-	shadowxscale /= SHORT(patch->width);
+	shadowyscale = min(shadowyscale, shadowxscale) / patch->height;
+	shadowxscale /= patch->width;
 	shadowskew = 0;
 
 	if (groundslope)
-	{
-		// haha let's try some dumb stuff
-		fixed_t xslope, zslope;
-		angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - groundslope->xydirection) >> ANGLETOFINESHIFT;
-
-		xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta);
-		zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta);
+		R_SkewShadowSprite(thing, groundslope, groundz, patch->height, scalemul, &shadowyscale, &shadowskew);
 
-		//CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope);
-
-		if (viewz < groundz)
-			shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope);
-		else
-			shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope);
-
-		shadowyscale = abs(shadowyscale);
-
-		shadowskew = xslope;
-	}
-
-	tx -= SHORT(patch->width) * shadowxscale/2;
+	tx -= patch->width * shadowxscale/2;
 	x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
 	if (x1 >= viewwidth) return;
 
-	tx += SHORT(patch->width) * shadowxscale;
+	tx += patch->width * shadowxscale;
 	x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
 	if (x2 < 0 || x2 <= x1) return;
 
-	if (shadowyscale < FRACUNIT/SHORT(patch->height)) return; // fix some crashes?
+	if (shadowyscale < FRACUNIT/patch->height) return; // fix some crashes?
 
 	shadow = R_NewVisSprite();
 	shadow->patch = patch;
@@ -1293,8 +1342,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	shadow->dispoffset = vis->dispoffset - 5;
 	shadow->gx = thing->x;
 	shadow->gy = thing->y;
-	shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + SHORT(patch->height) * shadowyscale / 2;
-	shadow->gz = shadow->gzt - SHORT(patch->height) * shadowyscale;
+	shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + patch->height * shadowyscale / 2;
+	shadow->gz = shadow->gzt - patch->height * shadowyscale;
 	shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale));
 	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
 		shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale);
@@ -1315,7 +1364,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 
 	shadow->startfrac = 0;
 	//shadow->xiscale = 0x7ffffff0 / (shadow->xscale/2);
-	shadow->xiscale = (SHORT(patch->width)<<FRACBITS)/(x2-x1+1); // fuck it
+	shadow->xiscale = (patch->width<<FRACBITS)/(x2-x1+1); // fuck it
 
 	if (shadow->x1 > x1)
 		shadow->startfrac += shadow->xiscale*(shadow->x1-x1);
@@ -1374,13 +1423,15 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	size_t frame, rot;
 	UINT16 flip;
-	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
+	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing));
 	boolean mirrored = thing->mirrored;
-	boolean hflip = (!(thing->frame & FF_HORIZONTALFLIP) != !mirrored);
+	boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
 
 	INT32 lindex;
+	INT32 trans;
 
 	vissprite_t *vis;
+	patch_t *patch;
 
 	spritecut_e cut = SC_NONE;
 
@@ -1389,10 +1440,15 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t scalestep;
 	fixed_t offset, offset2;
 
+	fixed_t sheartan = 0;
+	fixed_t shadowscale = FRACUNIT;
 	fixed_t basetx; // drop shadows
 
-	boolean papersprite = !!(thing->frame & FF_PAPERSPRITE);
-	fixed_t paperoffset = 0, paperdistance = 0; angle_t centerangle = 0;
+	boolean shadowdraw, shadoweffects, shadowskew;
+	boolean splat = R_ThingIsFloorSprite(thing);
+	boolean papersprite = (R_ThingIsPaperSprite(thing) && !splat);
+	fixed_t paperoffset = 0, paperdistance = 0;
+	angle_t centerangle = 0;
 
 	INT32 dispoffset = thing->info->dispoffset;
 
@@ -1401,10 +1457,12 @@ static void R_ProjectSprite(mobj_t *thing)
 	INT32 heightsec, phs;
 	INT32 light = 0;
 	fixed_t this_scale = thing->scale;
+	fixed_t spritexscale, spriteyscale;
 
 	// rotsprite
 	fixed_t spr_width, spr_height;
 	fixed_t spr_offset, spr_topoffset;
+
 #ifdef ROTSPRITE
 	patch_t *rotsprite = NULL;
 	INT32 rollangle = 0;
@@ -1533,17 +1591,28 @@ static void R_ProjectSprite(mobj_t *thing)
 	spr_offset = spritecachedinfo[lump].offset;
 	spr_topoffset = spritecachedinfo[lump].topoffset;
 
+	//Fab: lumppat is the lump number of the patch to use, this is different
+	//     than lumpid for sprites-in-pwad : the graphics are patched
+	patch = W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
+
 #ifdef ROTSPRITE
-	if (thing->rollangle)
+	if (thing->rollangle
+	&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
 	{
 		rollangle = R_GetRollAngle(thing->rollangle);
-		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, sprinfo, rollangle);
+		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
+
 		if (rotsprite != NULL)
 		{
-			spr_width = SHORT(rotsprite->width) << FRACBITS;
-			spr_height = SHORT(rotsprite->height) << FRACBITS;
-			spr_offset = SHORT(rotsprite->leftoffset) << FRACBITS;
-			spr_topoffset = SHORT(rotsprite->topoffset) << FRACBITS;
+			patch = rotsprite;
+			cut |= SC_ISROTATED;
+
+			spr_width = rotsprite->width << FRACBITS;
+			spr_height = rotsprite->height << FRACBITS;
+			spr_offset = rotsprite->leftoffset << FRACBITS;
+			spr_topoffset = rotsprite->topoffset << FRACBITS;
+			spr_topoffset += FEETADJUST;
+
 			// flip -> rotate, not rotate -> flip
 			flip = 0;
 		}
@@ -1553,12 +1622,18 @@ static void R_ProjectSprite(mobj_t *thing)
 	flip = !flip != !hflip;
 
 	// calculate edges of the shape
+	spritexscale = thing->spritexscale;
+	spriteyscale = thing->spriteyscale;
+	if (spritexscale < 1 || spriteyscale < 1)
+		return;
+
 	if (flip)
 		offset = spr_offset - spr_width;
 	else
 		offset = -spr_offset;
-	offset = FixedMul(offset, this_scale);
-	offset2 = FixedMul(spr_width, this_scale);
+
+	offset = FixedMul(offset, FixedMul(spritexscale, this_scale));
+	offset2 = FixedMul(spr_width, FixedMul(spritexscale, this_scale));
 
 	if (papersprite)
 	{
@@ -1686,7 +1761,7 @@ static void R_ProjectSprite(mobj_t *thing)
 			dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
 
 		sortscale = linkscale; // now make sure it's linked
-		cut = SC_LINKDRAW;
+		cut |= SC_LINKDRAW;
 	}
 
 	// PORTAL SPRITE CLIPPING
@@ -1699,19 +1774,87 @@ static void R_ProjectSprite(mobj_t *thing)
 			return;
 	}
 
-	//SoM: 3/17/2000: Disregard sprites that are out of view..
-	if (vflip)
+	// Determine the translucency value.
+	if (oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility)
+		trans = tr_trans80; // because now the translucency is set through FF_TRANSMASK
+	else if (oldthing->frame & FF_TRANSMASK)
+		trans = (oldthing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
+	else
+		trans = 0;
+
+	// Check if this sprite needs to be rendered like a shadow
+	shadowdraw = (!!(thing->renderflags & RF_SHADOWDRAW) && !(papersprite || splat));
+	shadoweffects = (thing->renderflags & RF_SHADOWEFFECTS);
+	shadowskew = (shadowdraw && thing->standingslope);
+
+	if (shadowdraw || shadoweffects)
 	{
-		// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
-		// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
-		// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
-		gz = oldthing->z + oldthing->height - FixedMul(spr_topoffset, this_scale);
-		gzt = gz + FixedMul(spr_height, this_scale);
+		fixed_t groundz = R_GetShadowZ(thing, NULL);
+		boolean isflipped = (thing->eflags & MFE_VERTICALFLIP);
+
+		if (shadoweffects)
+		{
+			mobj_t *caster = thing->target;
+
+			if (caster && !P_MobjWasRemoved(caster))
+			{
+				fixed_t floordiff;
+
+				if (abs(groundz-viewz)/tz > 4)
+					return; // Prevent stretchy shadows and possible crashes
+
+				floordiff = abs((isflipped ? caster->height : 0) + caster->z - groundz);
+				trans += ((floordiff / (100*FRACUNIT)) + 3);
+				shadowscale = FixedMul(FRACUNIT - floordiff/640, caster->scale);
+			}
+			else
+				trans += 3;
+
+			if (trans >= 9)
+				return;
+
+			trans--;
+		}
+
+		if (shadowdraw)
+		{
+			spritexscale = FixedMul(thing->radius * 2, shadowscale);
+			spriteyscale = FixedMul(thing->radius * 2, shadowscale);
+			spriteyscale = FixedMul(spriteyscale, FixedDiv(abs(groundz - viewz), tz));
+			spriteyscale = min(spriteyscale, spritexscale) / patch->height;
+		}
+		else
+			spritexscale = spriteyscale = shadowscale;
+
+		spritexscale /= patch->width;
+
+		if (shadowskew)
+		{
+			R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan);
+
+			gzt = (isflipped ? (thing->z + thing->height) : thing->z) + patch->height * spriteyscale / 2;
+			gz = gzt - patch->height * spriteyscale;
+
+			cut |= SC_SHEAR;
+		}
 	}
-	else
+
+	if (!shadowskew)
 	{
-		gzt = oldthing->z + FixedMul(spr_topoffset, this_scale);
-		gz = gzt - FixedMul(spr_height, this_scale);
+		//SoM: 3/17/2000: Disregard sprites that are out of view..
+		if (vflip)
+		{
+			// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
+			// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
+			// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
+			gz = oldthing->z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
+			gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
+		}
+		else
+		{
+			gzt = oldthing->z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
+			gz = gzt - FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
+		}
 	}
 
 	if (thing->subsector->sector->cullheight)
@@ -1764,9 +1907,10 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// store information in a vissprite
 	vis = R_NewVisSprite();
+	vis->renderflags = thing->renderflags;
+	vis->rotateflags = sprframe->rotate;
 	vis->heightsec = heightsec; //SoM: 3/17/2000
 	vis->mobjflags = thing->flags;
-	vis->scale = yscale; //<<detailshift;
 	vis->sortscale = sortscale;
 	vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15
 	vis->gx = thing->x;
@@ -1776,12 +1920,12 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->thingheight = thing->height;
 	vis->pz = thing->z;
 	vis->pzt = vis->pz + vis->thingheight;
-	vis->texturemid = vis->gzt - viewz;
+	vis->texturemid = FixedDiv(gzt - viewz, spriteyscale);
 	vis->scalestep = scalestep;
 	vis->paperoffset = paperoffset;
 	vis->paperdistance = paperdistance;
 	vis->centerangle = centerangle;
-	vis->shear.tan = 0;
+	vis->shear.tan = sheartan;
 	vis->shear.offset = 0;
 
 	vis->mobj = thing; // Easy access! Tails 06-07-2002
@@ -1789,17 +1933,28 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
 	vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
 
-	vis->xscale = xscale; //SoM: 4/17/2000
 	vis->sector = thing->subsector->sector;
 	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
 	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
 	vis->cut = cut;
+
 	if (thing->subsector->sector->numlights)
 		vis->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
 	else
 		vis->extra_colormap = thing->subsector->sector->extra_colormap;
 
-	iscale = FixedDiv(FRACUNIT, xscale);
+	vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000
+	vis->scale = FixedMul(spriteyscale, yscale); //<<detailshift;
+
+	if (shadowdraw || shadoweffects)
+	{
+		iscale = (patch->width<<FRACBITS)/(x2-x1+1); // fuck it
+		x1 += (x2-x1)/2; // reusing x1 variable
+		vis->shear.offset = vis->x1-x1;
+		vis->shadowscale = shadowscale;
+	}
+	else
+		iscale = FixedDiv(FRACUNIT, vis->xscale);
 
 	if (flip)
 	{
@@ -1818,14 +1973,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		vis->scale += scalestep*(vis->x1 - x1);
 	}
 
-	//Fab: lumppat is the lump number of the patch to use, this is different
-	//     than lumpid for sprites-in-pwad : the graphics are patched
-#ifdef ROTSPRITE
-	if (rotsprite != NULL)
-		vis->patch = rotsprite;
-	else
-#endif
-		vis->patch = W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
+	vis->patch = patch;
 
 //
 // determine the colormap (lightlevel & special effects)
@@ -1835,13 +1983,13 @@ static void R_ProjectSprite(mobj_t *thing)
 	// specific translucency
 	if (!cv_translucency.value)
 		; // no translucency
-	else if (oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility)
-		vis->transmap = transtables + ((tr_trans80-1)<<FF_TRANSSHIFT); // because now the translucency is set through FF_TRANSMASK
-	else if (oldthing->frame & FF_TRANSMASK)
-		vis->transmap = transtables + (oldthing->frame & FF_TRANSMASK) - 0x10000;
+	else if (trans)
+		vis->transmap = transtables + ((trans-1)<<FF_TRANSSHIFT);
 
-	if (oldthing->frame & FF_FULLBRIGHT || oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW)
+	if (R_ThingIsFullBright(oldthing) || oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW)
 		vis->cut |= SC_FULLBRIGHT;
+	else if (R_ThingIsFullDark(oldthing))
+		vis->cut |= SC_FULLDARK;
 
 	if (vis->cut & SC_FULLBRIGHT
 		&& (!vis->extra_colormap || !(vis->extra_colormap->flags & CMF_FADEFULLBRIGHTSPRITES)))
@@ -1849,6 +1997,8 @@ static void R_ProjectSprite(mobj_t *thing)
 		// full bright: goggles
 		vis->colormap = colormaps;
 	}
+	else if (vis->cut & SC_FULLDARK)
+		vis->colormap = scalelight[0][0];
 	else
 	{
 		// diminished light
@@ -1862,8 +2012,10 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (vflip)
 		vis->cut |= SC_VFLIP;
+	if (splat)
+		vis->cut |= SC_SPLAT; // I like ya cut g
 
-	if (thing->subsector->sector->numlights)
+	if (thing->subsector->sector->numlights && !(shadowdraw || splat))
 		R_SplitSprite(vis);
 
 	if (oldthing->shadowscale && cv_shadow.value)
@@ -2470,10 +2622,13 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps
 			}
 			else if (r2->sprite)
 			{
-				if (r2->sprite->x1 > rover->x2 || r2->sprite->x2 < rover->x1)
-					continue;
-				if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
-					continue;
+				if (!(r2->sprite->cut & SC_SPLAT || rover->cut & SC_SPLAT))
+				{
+					if (r2->sprite->x1 > rover->x2 || r2->sprite->x2 < rover->x1)
+						continue;
+					if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
+						continue;
+				}
 
 				if (r2->sprite->sortscale > rover->sortscale
 				 || (r2->sprite->sortscale == rover->sortscale && r2->sprite->dispoffset > rover->dispoffset))
@@ -2552,6 +2707,146 @@ void R_InitDrawNodes(void)
 	nodebankhead.next = nodebankhead.prev = &nodebankhead;
 }
 
+static void R_DrawVisSplat(vissprite_t *spr)
+{
+#ifdef FLOORSPLATS
+	floorsplat_t splat;
+	fixed_t tr_x, tr_y, rot_x, rot_y, rot_z;
+	vertex_t *v3d;
+	vertex_t v2d[4];
+	fixed_t x, y;
+	fixed_t w, h;
+	angle_t splatangle, angle;
+	fixed_t ca, sa;
+	fixed_t xscale, yscale;
+	fixed_t xoffset, yoffset;
+	fixed_t leftoffset, topoffset;
+	boolean hflip = (spr->xiscale < 0);
+	boolean vflip = (spr->cut & SC_VFLIP);
+	UINT8 flipflags = 0;
+	vector2_t rotated[4];
+	INT32 i;
+
+	if (hflip)
+		flipflags |= PICFLAGS_XFLIP;
+	if (vflip)
+		flipflags |= PICFLAGS_YFLIP;
+
+	Patch_GenerateFlat(spr->patch, flipflags);
+	splat.pic = spr->patch->flats[flipflags];
+	if (splat.pic == NULL)
+		return;
+
+	splat.mobj = spr->mobj;
+	splat.width = spr->patch->width;
+	splat.height = spr->patch->height;
+	splat.scale = spr->mobj->scale;
+
+	if (spr->renderflags & RF_SHADOWEFFECTS)
+		splat.scale = FixedMul(splat.scale, spr->shadowscale);
+
+	if (spr->rotateflags & SRF_3D || spr->renderflags & RF_NOSPLATBILLBOARD)
+		splatangle = spr->mobj->angle;
+	else
+		splatangle = viewangle;
+
+	if (!(spr->cut & SC_ISROTATED))
+		splatangle += spr->mobj->rollangle;
+
+	splat.angle = -splatangle;
+	splat.angle += ANGLE_90;
+
+	topoffset = (spr->patch->topoffset * FRACUNIT);
+	leftoffset = (spr->patch->leftoffset * FRACUNIT);
+	if (hflip)
+		leftoffset = ((splat.width * FRACUNIT) - leftoffset);
+
+	xscale = spr->mobj->spritexscale;
+	yscale = spr->mobj->spriteyscale;
+
+	splat.xscale = FixedMul(splat.scale, xscale);
+	splat.yscale = FixedMul(splat.scale, yscale);
+
+	xoffset = FixedMul(leftoffset, splat.xscale);
+	yoffset = FixedMul(topoffset, splat.yscale);
+
+	x = spr->mobj->x;
+	y = spr->mobj->y;
+	w = (splat.width * splat.xscale);
+	h = (splat.height * splat.yscale);
+
+	splat.x = x;
+	splat.y = y;
+	splat.z = spr->mobj->z;
+
+	// Set positions
+
+	// 3--2
+	// |  |
+	// 0--1
+
+	splat.verts[0].x = w - xoffset;
+	splat.verts[0].y = yoffset;
+
+	splat.verts[1].x = -xoffset;
+	splat.verts[1].y = yoffset;
+
+	splat.verts[2].x = -xoffset;
+	splat.verts[2].y = -h + yoffset;
+
+	splat.verts[3].x = w - xoffset;
+	splat.verts[3].y = -h + yoffset;
+
+	angle = -splat.angle;
+	ca = FINECOSINE(angle>>ANGLETOFINESHIFT);
+	sa = FINESINE(angle>>ANGLETOFINESHIFT);
+
+	// Rotate
+	for (i = 0; i < 4; i++)
+	{
+		rotated[i].x = FixedMul(splat.verts[i].x, ca) - FixedMul(splat.verts[i].y, sa);
+		rotated[i].y = FixedMul(splat.verts[i].x, sa) + FixedMul(splat.verts[i].y, ca);
+	}
+
+	// Translate
+	for (i = 0; i < 4; i++)
+	{
+		splat.verts[i].x = rotated[i].x + x;
+		splat.verts[i].y = rotated[i].y + y;
+	}
+
+	rot_z = splat.z - viewz;
+
+	for (i = 0; i < 4; i++)
+	{
+		v3d = &splat.verts[i];
+
+		// transform the origin point
+		tr_x = v3d->x - viewx;
+		tr_y = v3d->y - viewy;
+
+		// rotation around vertical y axis
+		rot_x = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
+		rot_y = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
+
+		if (!rot_y || rot_y < FixedDiv(4*FRACUNIT, splat.scale))
+			return;
+
+		// note: y from view above of map, is distance far away
+		xscale = FixedDiv(projection, rot_y);
+		yscale = -FixedDiv(projectiony, rot_y);
+
+		// projection
+		v2d[i].x = (centerxfrac + FixedMul(rot_x, xscale))>>FRACBITS;
+		v2d[i].y = (centeryfrac + FixedMul(rot_z, yscale))>>FRACBITS;
+	}
+
+	R_RenderFloorSplat(&splat, v2d, spr);
+#else
+	(void)spr;
+#endif
+}
+
 //
 // R_DrawSprite
 //
@@ -2562,7 +2857,11 @@ static void R_DrawSprite(vissprite_t *spr)
 {
 	mfloorclip = spr->clipbot;
 	mceilingclip = spr->cliptop;
-	R_DrawVisSprite(spr);
+
+	if (spr->cut & SC_SPLAT)
+		R_DrawVisSplat(spr);
+	else
+		R_DrawVisSprite(spr);
 }
 
 // Special drawer for precipitation sprites Tails 08-18-2002
@@ -2573,207 +2872,210 @@ static void R_DrawPrecipitationSprite(vissprite_t *spr)
 	R_DrawPrecipitationVisSprite(spr);
 }
 
-// R_ClipSprites
+// R_ClipVisSprite
 // Clips vissprites without drawing, so that portals can work. -Red
-void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
+void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, portal_t* portal)
 {
-	vissprite_t *spr;
-	for (; clippedvissprites < visspritecount; clippedvissprites++)
-	{
-		drawseg_t *ds;
-		INT32		x;
-		INT32		r1;
-		INT32		r2;
-		fixed_t		scale;
-		fixed_t		lowscale;
-		INT32		silhouette;
-
-		spr = R_GetVisSprite(clippedvissprites);
-
-		for (x = spr->x1; x <= spr->x2; x++)
-			spr->clipbot[x] = spr->cliptop[x] = -2;
-
-		// Scan drawsegs from end to start for obscuring segs.
-		// The first drawseg that has a greater scale
-		//  is the clip seg.
-		//SoM: 4/8/2000:
-		// Pointer check was originally nonportable
-		// and buggy, by going past LEFT end of array:
+	drawseg_t *ds;
+	INT32		x;
+	INT32		r1;
+	INT32		r2;
+	fixed_t		scale;
+	fixed_t		lowscale;
+	INT32		silhouette;
+
+	for (x = x1; x <= x2; x++)
+		spr->clipbot[x] = spr->cliptop[x] = -2;
+
+	// Scan drawsegs from end to start for obscuring segs.
+	// The first drawseg that has a greater scale
+	//  is the clip seg.
+	//SoM: 4/8/2000:
+	// Pointer check was originally nonportable
+	// and buggy, by going past LEFT end of array:
+
+	//    for (ds = ds_p-1; ds >= drawsegs; ds--)    old buggy code
+	for (ds = ds_p; ds-- > dsstart;)
+	{
+		// determine if the drawseg obscures the sprite
+		if (ds->x1 > x2 ||
+			ds->x2 < x1 ||
+			(!ds->silhouette
+			 && !ds->maskedtexturecol))
+		{
+			// does not cover sprite
+			continue;
+		}
 
-		//    for (ds = ds_p-1; ds >= drawsegs; ds--)    old buggy code
-		for (ds = ds_p; ds-- > dsstart;)
+		if (ds->portalpass != 66)
 		{
-			// determine if the drawseg obscures the sprite
-			if (ds->x1 > spr->x2 ||
-			    ds->x2 < spr->x1 ||
-			    (!ds->silhouette
-			     && !ds->maskedtexturecol))
+			if (ds->portalpass > 0 && ds->portalpass <= portalrender)
+				continue; // is a portal
+
+			if (ds->scale1 > ds->scale2)
 			{
-				// does not cover sprite
-				continue;
+				lowscale = ds->scale2;
+				scale = ds->scale1;
 			}
-
-			if (ds->portalpass != 66)
+			else
 			{
-				if (ds->portalpass > 0 && ds->portalpass <= portalrender)
-					continue; // is a portal
-
-				if (ds->scale1 > ds->scale2)
-				{
-					lowscale = ds->scale2;
-					scale = ds->scale1;
-				}
-				else
-				{
-					lowscale = ds->scale1;
-					scale = ds->scale2;
-				}
+				lowscale = ds->scale1;
+				scale = ds->scale2;
+			}
 
-				if (scale < spr->sortscale ||
-					(lowscale < spr->sortscale &&
-					 !R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
-				{
-					// masked mid texture?
-					/*if (ds->maskedtexturecol)
-						R_RenderMaskedSegRange (ds, r1, r2);*/
-					// seg is behind sprite
-					continue;
-				}
+			if (scale < spr->sortscale ||
+				(lowscale < spr->sortscale &&
+				 !R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
+			{
+				// masked mid texture?
+				/*if (ds->maskedtexturecol)
+					R_RenderMaskedSegRange (ds, r1, r2);*/
+				// seg is behind sprite
+				continue;
 			}
+		}
 
-			r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1;
-			r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2;
+		r1 = ds->x1 < x1 ? x1 : ds->x1;
+		r2 = ds->x2 > x2 ? x2 : ds->x2;
 
-			// clip this piece of the sprite
-			silhouette = ds->silhouette;
+		// clip this piece of the sprite
+		silhouette = ds->silhouette;
 
-			if (spr->gz >= ds->bsilheight)
-				silhouette &= ~SIL_BOTTOM;
+		if (spr->gz >= ds->bsilheight)
+			silhouette &= ~SIL_BOTTOM;
 
-			if (spr->gzt <= ds->tsilheight)
-				silhouette &= ~SIL_TOP;
+		if (spr->gzt <= ds->tsilheight)
+			silhouette &= ~SIL_TOP;
 
-			if (silhouette == SIL_BOTTOM)
+		if (silhouette == SIL_BOTTOM)
+		{
+			// bottom sil
+			for (x = r1; x <= r2; x++)
+				if (spr->clipbot[x] == -2)
+					spr->clipbot[x] = ds->sprbottomclip[x];
+		}
+		else if (silhouette == SIL_TOP)
+		{
+			// top sil
+			for (x = r1; x <= r2; x++)
+				if (spr->cliptop[x] == -2)
+					spr->cliptop[x] = ds->sprtopclip[x];
+		}
+		else if (silhouette == (SIL_TOP|SIL_BOTTOM))
+		{
+			// both
+			for (x = r1; x <= r2; x++)
 			{
-				// bottom sil
-				for (x = r1; x <= r2; x++)
-					if (spr->clipbot[x] == -2)
-						spr->clipbot[x] = ds->sprbottomclip[x];
+				if (spr->clipbot[x] == -2)
+					spr->clipbot[x] = ds->sprbottomclip[x];
+				if (spr->cliptop[x] == -2)
+					spr->cliptop[x] = ds->sprtopclip[x];
 			}
-			else if (silhouette == SIL_TOP)
-			{
-				// top sil
-				for (x = r1; x <= r2; x++)
-					if (spr->cliptop[x] == -2)
-						spr->cliptop[x] = ds->sprtopclip[x];
+		}
+	}
+	//SoM: 3/17/2000: Clip sprites in water.
+	if (spr->heightsec != -1)  // only things in specially marked sectors
+	{
+		fixed_t mh, h;
+		INT32 phs = viewplayer->mo->subsector->sector->heightsec;
+		if ((mh = sectors[spr->heightsec].floorheight) > spr->gz &&
+			(h = centeryfrac - FixedMul(mh -= viewz, spr->sortscale)) >= 0 &&
+			(h >>= FRACBITS) < viewheight)
+		{
+			if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight))
+			{                          // clip bottom
+				for (x = x1; x <= x2; x++)
+					if (spr->clipbot[x] == -2 || h < spr->clipbot[x])
+						spr->clipbot[x] = (INT16)h;
 			}
-			else if (silhouette == (SIL_TOP|SIL_BOTTOM))
+			else						// clip top
 			{
-				// both
-				for (x = r1; x <= r2; x++)
-				{
-					if (spr->clipbot[x] == -2)
-						spr->clipbot[x] = ds->sprbottomclip[x];
-					if (spr->cliptop[x] == -2)
-						spr->cliptop[x] = ds->sprtopclip[x];
-				}
+				for (x = x1; x <= x2; x++)
+					if (spr->cliptop[x] == -2 || h > spr->cliptop[x])
+						spr->cliptop[x] = (INT16)h;
 			}
 		}
-		//SoM: 3/17/2000: Clip sprites in water.
-		if (spr->heightsec != -1)  // only things in specially marked sectors
+
+		if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt &&
+			(h = centeryfrac - FixedMul(mh-viewz, spr->sortscale)) >= 0 &&
+			(h >>= FRACBITS) < viewheight)
 		{
-			fixed_t mh, h;
-			INT32 phs = viewplayer->mo->subsector->sector->heightsec;
-			if ((mh = sectors[spr->heightsec].floorheight) > spr->gz &&
-				(h = centeryfrac - FixedMul(mh -= viewz, spr->sortscale)) >= 0 &&
-				(h >>= FRACBITS) < viewheight)
-			{
-				if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight))
-				{                          // clip bottom
-					for (x = spr->x1; x <= spr->x2; x++)
-						if (spr->clipbot[x] == -2 || h < spr->clipbot[x])
-							spr->clipbot[x] = (INT16)h;
-				}
-				else						// clip top
-				{
-					for (x = spr->x1; x <= spr->x2; x++)
-						if (spr->cliptop[x] == -2 || h > spr->cliptop[x])
-							spr->cliptop[x] = (INT16)h;
-				}
+			if (phs != -1 && viewz >= sectors[phs].ceilingheight)
+			{                         // clip bottom
+				for (x = x1; x <= x2; x++)
+					if (spr->clipbot[x] == -2 || h < spr->clipbot[x])
+						spr->clipbot[x] = (INT16)h;
 			}
-
-			if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt &&
-			    (h = centeryfrac - FixedMul(mh-viewz, spr->sortscale)) >= 0 &&
-			    (h >>= FRACBITS) < viewheight)
+			else                       // clip top
 			{
-				if (phs != -1 && viewz >= sectors[phs].ceilingheight)
-				{                         // clip bottom
-					for (x = spr->x1; x <= spr->x2; x++)
-						if (spr->clipbot[x] == -2 || h < spr->clipbot[x])
-							spr->clipbot[x] = (INT16)h;
-				}
-				else                       // clip top
-				{
-					for (x = spr->x1; x <= spr->x2; x++)
-						if (spr->cliptop[x] == -2 || h > spr->cliptop[x])
-							spr->cliptop[x] = (INT16)h;
-				}
+				for (x = x1; x <= x2; x++)
+					if (spr->cliptop[x] == -2 || h > spr->cliptop[x])
+						spr->cliptop[x] = (INT16)h;
 			}
 		}
-		if (spr->cut & SC_TOP && spr->cut & SC_BOTTOM)
+	}
+	if (spr->cut & SC_TOP && spr->cut & SC_BOTTOM)
+	{
+		for (x = x1; x <= x2; x++)
 		{
-			for (x = spr->x1; x <= spr->x2; x++)
-			{
-				if (spr->cliptop[x] == -2 || spr->szt > spr->cliptop[x])
-					spr->cliptop[x] = spr->szt;
+			if (spr->cliptop[x] == -2 || spr->szt > spr->cliptop[x])
+				spr->cliptop[x] = spr->szt;
 
-				if (spr->clipbot[x] == -2 || spr->sz < spr->clipbot[x])
-					spr->clipbot[x] = spr->sz;
-			}
+			if (spr->clipbot[x] == -2 || spr->sz < spr->clipbot[x])
+				spr->clipbot[x] = spr->sz;
 		}
-		else if (spr->cut & SC_TOP)
+	}
+	else if (spr->cut & SC_TOP)
+	{
+		for (x = x1; x <= x2; x++)
 		{
-			for (x = spr->x1; x <= spr->x2; x++)
-			{
-				if (spr->cliptop[x] == -2 || spr->szt > spr->cliptop[x])
-					spr->cliptop[x] = spr->szt;
-			}
+			if (spr->cliptop[x] == -2 || spr->szt > spr->cliptop[x])
+				spr->cliptop[x] = spr->szt;
 		}
-		else if (spr->cut & SC_BOTTOM)
+	}
+	else if (spr->cut & SC_BOTTOM)
+	{
+		for (x = x1; x <= x2; x++)
 		{
-			for (x = spr->x1; x <= spr->x2; x++)
-			{
-				if (spr->clipbot[x] == -2 || spr->sz < spr->clipbot[x])
-					spr->clipbot[x] = spr->sz;
-			}
+			if (spr->clipbot[x] == -2 || spr->sz < spr->clipbot[x])
+				spr->clipbot[x] = spr->sz;
 		}
+	}
 
-		// all clipping has been performed, so store the values - what, did you think we were drawing them NOW?
+	// all clipping has been performed, so store the values - what, did you think we were drawing them NOW?
 
-		// check for unclipped columns
-		for (x = spr->x1; x <= spr->x2; x++)
-		{
-			if (spr->clipbot[x] == -2)
-				spr->clipbot[x] = (INT16)viewheight;
+	// check for unclipped columns
+	for (x = x1; x <= x2; x++)
+	{
+		if (spr->clipbot[x] == -2)
+			spr->clipbot[x] = (INT16)viewheight;
 
-			if (spr->cliptop[x] == -2)
-				//Fab : 26-04-98: was -1, now clips against console bottom
-				spr->cliptop[x] = (INT16)con_clipviewtop;
-		}
+		if (spr->cliptop[x] == -2)
+			//Fab : 26-04-98: was -1, now clips against console bottom
+			spr->cliptop[x] = (INT16)con_clipviewtop;
+	}
 
-		if (portal)
+	if (portal)
+	{
+		for (x = x1; x <= x2; x++)
 		{
-			for (x = spr->x1; x <= spr->x2; x++)
-			{
-				if (spr->clipbot[x] > portal->floorclip[x - portal->start])
-					spr->clipbot[x] = portal->floorclip[x - portal->start];
-				if (spr->cliptop[x] < portal->ceilingclip[x - portal->start])
-					spr->cliptop[x] = portal->ceilingclip[x - portal->start];
-			}
+			if (spr->clipbot[x] > portal->floorclip[x - portal->start])
+				spr->clipbot[x] = portal->floorclip[x - portal->start];
+			if (spr->cliptop[x] < portal->ceilingclip[x - portal->start])
+				spr->cliptop[x] = portal->ceilingclip[x - portal->start];
 		}
 	}
 }
 
+void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
+{
+	for (; clippedvissprites < visspritecount; clippedvissprites++)
+	{
+		vissprite_t *spr = R_GetVisSprite(clippedvissprites);
+		R_ClipVisSprite(spr, spr->x1, spr->x2, dsstart, portal);
+	}
+}
+
 /* Check if thing may be drawn from our current view. */
 boolean R_ThingVisible (mobj_t *thing)
 {
@@ -2823,6 +3125,36 @@ boolean R_PrecipThingVisible (precipmobj_t *precipthing,
 	return ( approx_dist <= limit_dist );
 }
 
+boolean R_ThingHorizontallyFlipped(mobj_t *thing)
+{
+	return (thing->frame & FF_HORIZONTALFLIP || thing->renderflags & RF_HORIZONTALFLIP);
+}
+
+boolean R_ThingVerticallyFlipped(mobj_t *thing)
+{
+	return (thing->frame & FF_VERTICALFLIP || thing->renderflags & RF_VERTICALFLIP);
+}
+
+boolean R_ThingIsPaperSprite(mobj_t *thing)
+{
+	return (thing->frame & FF_PAPERSPRITE || thing->renderflags & RF_PAPERSPRITE);
+}
+
+boolean R_ThingIsFloorSprite(mobj_t *thing)
+{
+	return (thing->flags2 & MF2_SPLAT || thing->renderflags & RF_FLOORSPRITE);
+}
+
+boolean R_ThingIsFullBright (mobj_t *thing)
+{
+	return (thing->frame & FF_FULLBRIGHT || thing->renderflags & RF_FULLBRIGHT);
+}
+
+boolean R_ThingIsFullDark (mobj_t *thing)
+{
+	return (thing->renderflags & RF_FULLDARK);
+}
+
 //
 // R_DrawMasked
 //
diff --git a/src/r_things.h b/src/r_things.h
index f241a78ba507e081446c498283e08a0365da42a7..2addcb7ca1353f4f414bbfe0f3c0b409e1cef3db 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -65,7 +65,6 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope);
 void R_AddSprites(sector_t *sec, INT32 lightlevel);
 void R_InitSprites(void);
 void R_ClearSprites(void);
-void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
 
 boolean R_ThingVisible (mobj_t *thing);
 
@@ -76,6 +75,15 @@ boolean R_ThingVisibleWithinDist (mobj_t *thing,
 boolean R_PrecipThingVisible (precipmobj_t *precipthing,
 		fixed_t precip_draw_dist);
 
+boolean R_ThingHorizontallyFlipped (mobj_t *thing);
+boolean R_ThingVerticallyFlipped (mobj_t *thing);
+
+boolean R_ThingIsPaperSprite (mobj_t *thing);
+boolean R_ThingIsFloorSprite (mobj_t *thing);
+
+boolean R_ThingIsFullBright (mobj_t *thing);
+boolean R_ThingIsFullDark (mobj_t *thing);
+
 // --------------
 // MASKED DRAWING
 // --------------
@@ -108,19 +116,23 @@ void R_DrawMasked(maskcount_t* masks, UINT8 nummasks);
 typedef enum
 {
 	// actual cuts
-	SC_NONE = 0,
-	SC_TOP = 1,
-	SC_BOTTOM = 1<<1,
+	SC_NONE       = 0,
+	SC_TOP        = 1,
+	SC_BOTTOM     = 1<<1,
 	// other flags
-	SC_PRECIP = 1<<2,
-	SC_LINKDRAW = 1<<3,
+	SC_PRECIP     = 1<<2,
+	SC_LINKDRAW   = 1<<3,
 	SC_FULLBRIGHT = 1<<4,
-	SC_VFLIP = 1<<5,
-	SC_ISSCALED = 1<<6,
-	SC_SHADOW = 1<<7,
+	SC_FULLDARK   = 1<<5,
+	SC_VFLIP      = 1<<6,
+	SC_ISSCALED   = 1<<7,
+	SC_ISROTATED  = 1<<8,
+	SC_SHADOW     = 1<<9,
+	SC_SHEAR      = 1<<10,
+	SC_SPLAT      = 1<<11,
 	// masks
-	SC_CUTMASK = SC_TOP|SC_BOTTOM,
-	SC_FLAGMASK = ~SC_CUTMASK
+	SC_CUTMASK    = SC_TOP|SC_BOTTOM,
+	SC_FLAGMASK   = ~SC_CUTMASK
 } spritecut_e;
 
 // A vissprite_t is a thing that will be drawn during a refresh,
@@ -177,6 +189,10 @@ typedef struct vissprite_s
 	INT16 sz, szt;
 
 	spritecut_e cut;
+	UINT32 renderflags;
+	UINT8 rotateflags;
+
+	fixed_t shadowscale;
 
 	INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];
 
@@ -185,6 +201,12 @@ typedef struct vissprite_s
 
 extern UINT32 visspritecount;
 
+void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
+void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, portal_t* portal);
+
+boolean R_SpriteIsFlashing(vissprite_t *vis);
+UINT8 *R_GetSpriteTranslation(vissprite_t *vis);
+
 // ----------
 // DRAW NODES
 // ----------
diff --git a/src/screen.c b/src/screen.c
index adda4ba2f2cb0beda0c06ebeefea2495e7dfe6a0..f5d182f3488b4ebb821242084b75ea652a349531 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -124,6 +124,8 @@ void SCR_SetDrawFuncs(void)
 		spanfuncs[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_8;
 		spanfuncs[SPANDRAWFUNC_SPLAT] = R_DrawSplat_8;
 		spanfuncs[SPANDRAWFUNC_TRANSSPLAT] = R_DrawTranslucentSplat_8;
+		spanfuncs[SPANDRAWFUNC_SPRITE] = R_DrawFloorSprite_8;
+		spanfuncs[SPANDRAWFUNC_TRANSSPRITE] = R_DrawTranslucentFloorSprite_8;
 		spanfuncs[SPANDRAWFUNC_FOG] = R_DrawFogSpan_8;
 #ifndef NOWATER
 		spanfuncs[SPANDRAWFUNC_WATER] = R_DrawTranslucentWaterSpan_8;
@@ -140,6 +142,8 @@ void SCR_SetDrawFuncs(void)
 		spanfuncs_npo2[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_NPO2_8;
 		spanfuncs_npo2[SPANDRAWFUNC_SPLAT] = R_DrawSplat_NPO2_8;
 		spanfuncs_npo2[SPANDRAWFUNC_TRANSSPLAT] = R_DrawTranslucentSplat_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_SPRITE] = R_DrawFloorSprite_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_TRANSSPRITE] = R_DrawTranslucentFloorSprite_NPO2_8;
 		spanfuncs_npo2[SPANDRAWFUNC_FOG] = NULL; // Not needed
 #ifndef NOWATER
 		spanfuncs_npo2[SPANDRAWFUNC_WATER] = R_DrawTranslucentWaterSpan_NPO2_8;
diff --git a/src/screen.h b/src/screen.h
index acc08f00151aa1567d1ef137c7ba90e71550a039..021c644ba9792be0fc82365b638872ccb3d87dcd 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -142,6 +142,8 @@ enum
 	SPANDRAWFUNC_TRANS,
 	SPANDRAWFUNC_SPLAT,
 	SPANDRAWFUNC_TRANSSPLAT,
+	SPANDRAWFUNC_SPRITE,
+	SPANDRAWFUNC_TRANSSPRITE,
 	SPANDRAWFUNC_FOG,
 #ifndef NOWATER
 	SPANDRAWFUNC_WATER,
diff --git a/src/tmap.nas b/src/tmap.nas
index 106f38e962b03fef0f0edc9e93dbd71112449ced..69282d0b471dd2c86802df544f4a346e4b96baa9 100644
--- a/src/tmap.nas
+++ b/src/tmap.nas
@@ -763,8 +763,8 @@ TX2             EQU    16
 TY2             EQU    20
 RASTERY_SIZEOF  EQU    24
 
-cglobal rasterize_segment_tex
-rasterize_segment_tex:
+cglobal rasterize_segment_tex_asm
+rasterize_segment_tex_asm:
         push    ebp
         mov     ebp,esp