// Emacs style mode select	 -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// DESCRIPTION:
//		All the clipping: columns, horizontal spans, sky columns.
//
// This file contains some code from the Build Engine.
//
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
//-----------------------------------------------------------------------------

#include "m_alloc.h"
#include <stdlib.h>
#include <stddef.h>

#include "templates.h"
#include "i_system.h"

#include "doomdef.h"
#include "doomstat.h"
#include "p_lnspec.h"

#include "r_local.h"
#include "r_sky.h"
#include "v_video.h"

#include "m_swap.h"
#include "w_wad.h"
#include "stats.h"

#define WALLYREPEAT 8

CVAR (Int, ty, 8, 0)
CVAR (Int, tx, 8, 0)

#define HEIGHTBITS 12
#define HEIGHTSHIFT (FRACBITS-HEIGHTBITS)

// The 3072 below is just an arbitrary value picked to avoid
// drawing lines the player is too close to that would overflow
// the texture calculations.
#define TOO_CLOSE_Z 3072

extern fixed_t globaluclip, globaldclip;


// OPTIMIZE: closed two sided lines as single sided

// killough 1/6/98: replaced globals with statics where appropriate

static BOOL		segtextured;	// True if any of the segs textures might be visible.
static bool		markfloor;		// False if the back side is the same plane.
static bool		markceiling;
static FTexture *toptexture;
static FTexture *bottomtexture;
static FTexture *midtexture;

int OWallMost (short *mostbuf, fixed_t z);
int WallMost (short *mostbuf, const secplane_t &plane);
void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat);
void PrepLWall (fixed_t *lwall, fixed_t walxrepeat);
extern fixed_t WallSZ1, WallSZ2, WallTX1, WallTX2, WallTY1, WallTY2, WallCX1, WallCX2, WallCY1, WallCY2;
extern int WallSX1, WallSX2;
extern float WallUoverZorg, WallUoverZstep, WallInvZorg, WallInvZstep, WallDepthScale, WallDepthOrg;

int		wallshade;

short	walltop[MAXWIDTH];	// [RH] record max extents of wall
short	wallbottom[MAXWIDTH];
short	wallupper[MAXWIDTH];
short	walllower[MAXWIDTH];
fixed_t	swall[MAXWIDTH];
fixed_t	lwall[MAXWIDTH];
int		lwallscale;

//
// regular wall
//
extern fixed_t	rw_backcz1, rw_backcz2;
extern fixed_t	rw_backfz1, rw_backfz2;
extern fixed_t	rw_frontcz1, rw_frontcz2;
extern fixed_t	rw_frontfz1, rw_frontfz2;

int				rw_ceilstat, rw_floorstat;
bool			rw_mustmarkfloor, rw_mustmarkceiling;
bool			rw_prepped;
bool			rw_markmirror;
bool			rw_havehigh;
bool			rw_havelow;

fixed_t			rw_light;		// [RH] Scale lights with viewsize adjustments
fixed_t			rw_lightstep;
fixed_t			rw_lightleft;

static fixed_t	rw_frontlowertop;

static int		rw_x;
static int		rw_stopx;
fixed_t			rw_offset;
static fixed_t	rw_scalestep;
static fixed_t	rw_midtexturemid;
static fixed_t	rw_toptexturemid;
static fixed_t	rw_bottomtexturemid;

FTexture		*rw_pic;

static short	*maskedtexturecol;
static FTexture	*WallSpriteTile;

static void R_RenderBoundWallSprite (AActor *first, drawseg_t *clipper, int pass);
static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans));

//=============================================================================
//
// CVAR r_fogboundary
//
// If true, makes fog look more "real" by shading the walls separating two
// sectors with different fog.
//=============================================================================

CVAR(Bool, r_fogboundary, true, 0)

inline bool IsFogBoundary (sector_t *front, sector_t *back)
{
	return r_fogboundary && !fixedcolormap && front->ColorMap->Fade &&
		front->ColorMap->Fade != back->ColorMap->Fade &&
		(front->ceilingpic != skyflatnum || back->ceilingpic != skyflatnum);
}

//=============================================================================
//
// CVAR r_drawmirrors
//
// Set to false to disable rendering of mirrors
//=============================================================================

CVAR(Bool, r_drawmirrors, true, 0)

//
// R_RenderMaskedSegRange
//
fixed_t *MaskedSWall;

static void BlastMaskedColumn (void (*blastfunc)(const BYTE *pixels, const FTexture::Span *spans), FTexture *tex)
{
	if (maskedtexturecol[dc_x] != SHRT_MAX)
	{
		// calculate lighting
		if (!fixedcolormap)
		{
			dc_colormap = basecolormap + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT);
		}

		dc_iscale = MaskedSWall[dc_x];
		sprtopscreen = centeryfrac - FixedMul (dc_texturemid, spryscale);
		
		// killough 1/25/98: here's where Medusa came in, because
		// it implicitly assumed that the column was all one patch.
		// Originally, Doom did not construct complete columns for
		// multipatched textures, so there were no header or trailer
		// bytes in the column referred to below, which explains
		// the Medusa effect. The fix is to construct true columns
		// when forming multipatched textures (see r_data.c).

		// draw the texture
		const FTexture::Span *spans;
		const BYTE *pixels = tex->GetColumn (maskedtexturecol[dc_x], &spans);
		blastfunc (pixels, spans);
		maskedtexturecol[dc_x] = SHRT_MAX;
	}
	rw_light += rw_lightstep;
	spryscale += rw_scalestep;
}

void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2)
{
	FTexture	*tex;
	int			i;
	sector_t	tempsec;		// killough 4/13/98
	fixed_t		texheight, textop, scaley;

	sprflipvert = false;

	curline = ds->curline;

	// killough 4/11/98: draw translucent 2s normal textures
	// [RH] modified because we don't use user-definable translucency maps
	ESPSResult drawmode;

	drawmode = R_SetPatchStyle (curline->sidedef->Flags & WALLF_ADDTRANS ? STYLE_Add : STYLE_Translucent,
		curline->linedef->alpha < 255 ? curline->linedef->alpha<<8 : FRACUNIT,
		0, 0);

	if ((drawmode == DontDraw && !ds->bFogBoundary))
	{
		return;
	}

	NetUpdate ();

	frontsector = curline->frontsector;
	backsector = curline->backsector;

	tex = TexMan(curline->sidedef->midtexture);

	// killough 4/13/98: get correct lightlevel for 2s normal textures
	const sector_t *sec = R_FakeFlat (frontsector, &tempsec, NULL, NULL, false);

	basecolormap = sec->ColorMap->Maps;	// [RH] Set basecolormap

	wallshade = ds->shade;
	rw_lightstep = ds->lightstep;
	rw_light = ds->light + (x1 - ds->x1) * rw_lightstep;

	mfloorclip = openings + ds->sprbottomclip - ds->x1;
	mceilingclip = openings + ds->sprtopclip - ds->x1;

	// [RH] Draw fog partition
	if (ds->bFogBoundary)
	{
		R_DrawFogBoundary (x1, x2, mceilingclip, mfloorclip);
		if (ds->maskedtexturecol == -1)
		{
			goto clearfog;
		}
	}

	MaskedSWall = (fixed_t *)(openings + ds->swall) - ds->x1;
	maskedtexturecol = openings + ds->maskedtexturecol - ds->x1;
	spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1);
	rw_scalestep = ds->iscalestep;

	// find positioning
	scaley = tex->ScaleY ? tex->ScaleY : ty;
	texheight = SafeDivScale19 (tex->GetHeight(), scaley);
	if (curline->linedef->flags & ML_DONTPEGBOTTOM)
	{
		dc_texturemid = MAX (frontsector->floortexz, backsector->floortexz) + texheight;
	}
	else
	{
		dc_texturemid = MIN (frontsector->ceilingtexz, backsector->ceilingtexz);
	}
	if (tex->bWorldPanning)
	{
		// rowoffset is added before the MulScale3 so that the masked texture will
		// still be positioned in world units rather than texels.
		dc_texturemid += curline->sidedef->rowoffset - viewz;
		textop = dc_texturemid;
		dc_texturemid = MulScale3 (dc_texturemid, scaley);
	}
	else
	{
		// rowoffset is added outside the multiply so that it positions the texture
		// by texels instead of world units.
		textop = dc_texturemid - viewz + SafeDivScale3 (curline->sidedef->rowoffset, scaley);
		dc_texturemid = MulScale3 (dc_texturemid - viewz, scaley) + curline->sidedef->rowoffset;
	}

	// [RH] Don't bother drawing segs that are completely offscreen
	if (MulScale12 (globaldclip, ds->sz1) < -textop &&
		MulScale12 (globaldclip, ds->sz2) < -textop)
	{ // Texture top is below the bottom of the screen
		goto clearfog;
	}

	if (MulScale12 (globaluclip, ds->sz1) > texheight - textop &&
		MulScale12 (globaluclip, ds->sz2) > texheight - textop)
	{ // Texture bottom is above the top of the screen
		goto clearfog;
	}

	WallSZ1 = ds->sz1;
	WallSZ2 = ds->sz2;
	WallSX1 = ds->sx1;
	WallSX2 = ds->sx2;

	OWallMost (wallupper, textop);
	OWallMost (walllower, textop - texheight);

	for (i = x1; i <= x2; i++)
	{
		if (wallupper[i] < mceilingclip[i])
			wallupper[i] = mceilingclip[i];
	}
	for (i = x1; i <= x2; i++)
	{
		if (walllower[i] > mfloorclip[i])
			walllower[i] = mfloorclip[i];
	}
	mfloorclip = walllower;
	mceilingclip = wallupper;

	if (fixedlightlev)
		dc_colormap = basecolormap + fixedlightlev;
	else if (fixedcolormap)
		dc_colormap = fixedcolormap;

	// draw the columns one at a time
	if (drawmode == DoDraw0)
	{
		for (dc_x = x1; dc_x <= x2; ++dc_x)
		{
			BlastMaskedColumn (R_DrawMaskedColumn, tex);
		}
	}
	else
	{
		// [RH] Draw up to four columns at once
		int stop = (x2+1) & ~3;

		if (x1 > x2)
			goto clearfog;

		dc_x = x1;

		while ((dc_x < stop) && (dc_x & 3))
		{
			BlastMaskedColumn (R_DrawMaskedColumn, tex);
			dc_x++;
		}

		while (dc_x < stop)
		{
			rt_initcols();
			BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++;
			BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++;
			BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++;
			BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex);
			rt_draw4cols (dc_x - 3);
			dc_x++;
		}

		while (dc_x <= x2)
		{
			BlastMaskedColumn (R_DrawMaskedColumn, tex);
			dc_x++;
		}
	}

clearfog:
	R_FinishSetPatchStyle ();
	//if (ds->bFogBoundary)
	{
		clearbufshort (openings + ds->sprtopclip - ds->x1 + x1, x2-x1+1, viewheight);
	}
	return;
}

extern int *texturewidthmask;

// prevlineasm1 is like vlineasm1 but skips the loop if only drawing one pixel
inline fixed_t prevline1 (fixed_t vince, byte *colormap, int count, fixed_t vplce, const byte *bufplce, byte *dest)
{
	dc_iscale = vince;
	dc_colormap = colormap;
	dc_count = count;
	dc_texturefrac = vplce;
	dc_source = bufplce;
	dc_dest = dest;
	return doprevline1 ();
}

/*
esp+00 = edi
esp+04 = ebx
esp+08 = esi
esp+0c = ebp
esp+13 = bad
esp+14 = light
esp+18 = &uwal[x]
esp+1c = &lwal[x]
esp+20 = x2
esp+24 = &dwal[x]
esp+28 = 
esp+2c = xoffset
esp+30 = yrepeat
esp+34 = texturemid
esp+40 = y1ve[]
esp+50 = y2ve[]
esp+54 =
esp+58 =
esp+5c =
esp+60 = <return address>
esp+64 = uwal
esp+68 = dwal
esp+6c = swal
esp+70 = lwal
esp+74 = getcol
esi = x
ebp = x2
*/

void wallscan (int x1, int x2, short *uwal, short *dwal, fixed_t *swal, fixed_t *lwal,
			   const BYTE *(*getcol)(FTexture *tex, int x))
{
	long x, shiftval;
	long y1ve[4], y2ve[4], u4, d4, z;
	char bad;
	fixed_t light = rw_light - rw_lightstep;
	SDWORD yrepeat, texturemid, xoffset;

	// This function also gets used to draw skies. Unlike BUILD, skies are
	// drawn by visplane instead of by bunch, so these checks are invalid.
	//if ((uwal[x1] > viewheight) && (uwal[x2] > viewheight)) return;
	//if ((dwal[x1] < 0) && (dwal[x2] < 0)) return;

	if (rw_pic->UseType == FTexture::TEX_Null)
	{
		return;
	}

//extern cycle_t WallScanCycles;
//clock (WallScanCycles);

	rw_pic->GetHeight();	// Make sure texture size is loaded
	shiftval = rw_pic->HeightBits;
	setupvline (32-shiftval);
	yrepeat = (rw_pic->ScaleY ? rw_pic->ScaleY : ty) << (11 - shiftval);
	texturemid = dc_texturemid << (16 - shiftval);
	xoffset = rw_offset;

	x = x1;
	//while ((umost[x] > dmost[x]) && (x <= x2)) x++;

	if (fixedcolormap)
	{
		palookupoffse[0] = dc_colormap;
		palookupoffse[1] = dc_colormap;
		palookupoffse[2] = dc_colormap;
		palookupoffse[3] = dc_colormap;
	}

	for(; (x <= x2) && (x & 3); ++x)
	{
		light += rw_lightstep;
		y1ve[0] = uwal[x];//max(uwal[x],umost[x]);
		y2ve[0] = dwal[x];//min(dwal[x],dmost[x]);
		if (y2ve[0] <= y1ve[0]) continue;

		if (!fixedcolormap)
		{ // calculate lighting
			dc_colormap = basecolormap + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT);
		}

		dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS);
		dc_dest = ylookup[y1ve[0]] + x + dc_destorg;
		dc_iscale = swal[x] * yrepeat;
		dc_count = y2ve[0] - y1ve[0];
		dc_texturefrac = texturemid + FixedMul (dc_iscale, (y1ve[0]<<FRACBITS)-centeryfrac+FRACUNIT);

		dovline1();
	}

	for(; x <= x2-3; x += 4)
	{
		bad = 0;
		for (z = 3; z>= 0; --z)
		{
			y1ve[z] = uwal[x+z];//max(uwal[x+z],umost[x+z]);
			y2ve[z] = dwal[x+z];//min(dwal[x+z],dmost[x+z])-1;
			if (y2ve[z] <= y1ve[z]) { bad += 1<<z; continue; }

			bufplce[z] = getcol (rw_pic, (lwal[x+z] + xoffset) >> FRACBITS);
			vince[z] = swal[x+z] * yrepeat;
			vplce[z] = texturemid + FixedMul (vince[z], (y1ve[z]<<FRACBITS)-centeryfrac+FRACUNIT);
		}
		if (bad == 15)
		{
			light += rw_lightstep << 2;
			continue;
		}

		if (!fixedcolormap)
		{
			for (z = 0; z < 4; ++z)
			{
				light += rw_lightstep;
				palookupoffse[z] = basecolormap + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT);
			}
		}

		u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3]));
		d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3]));

		if ((bad != 0) || (u4 >= d4))
		{
			for (z = 0; z < 4; ++z)
			{
				if (!(bad & 1))
				{
					prevline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg);
				}
				bad >>= 1;
			}
			continue;
		}

		for (z = 0; z < 4; ++z)
		{
			if (u4 > y1ve[z])
			{
				vplce[z] = prevline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg);
			}
		}

		if (d4 > u4)
		{
			dc_count = d4-u4;
			dc_dest = ylookup[u4]+x+dc_destorg;
			dovline4();
		}

		BYTE *i = x+ylookup[d4]+dc_destorg;
		for (z = 0; z < 4; ++z)
		{
			if (y2ve[z] > d4)
			{
				prevline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z);
			}
		}
	}
	for(;x<=x2;x++)
	{
		light += rw_lightstep;
		y1ve[0] = uwal[x];//max(uwal[x],umost[x]);
		y2ve[0] = dwal[x];//min(dwal[x],dmost[x]);
		if (y2ve[0] <= y1ve[0]) continue;

		if (!fixedcolormap)
		{ // calculate lighting
			dc_colormap = basecolormap + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT);
		}

		dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS);
		dc_dest = ylookup[y1ve[0]] + x + dc_destorg;
		dc_iscale = swal[x] * yrepeat;
		dc_count = y2ve[0] - y1ve[0];
		dc_texturefrac = texturemid + FixedMul (dc_iscale, (y1ve[0]<<FRACBITS)-centeryfrac+FRACUNIT);

		dovline1();
	}

//unclock (WallScanCycles);

	NetUpdate ();
}

void wallscan_striped (int x1, int x2, short *uwal, short *dwal, fixed_t *swal, fixed_t *lwal)
{
	bool flooding = false;
	byte *startcolormap = basecolormap;
	int startshade = wallshade;
	bool fogginess = foggy;

	byte *floodcolormap = startcolormap;
	int floodshade = startshade;
	bool floodfoggy = foggy;

	short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH];
	short *up, *down;

	FExtraLight *el = frontsector->ExtraLights;

	up = uwal;
	down = most1;

	for (int i = 0; i < el->NumUsedLights; ++i)
	{
		if (flooding && !el->Lights[i].bFlooder)
		{
			continue;
		}

		if (!el->Lights[i].bOverlaps)
		{
			int j = WallMost (most3, el->Lights[i].Plane);

			if (most3[x1] > dwal[x1] && most3[x2] > dwal[x2])
			{ // Done - does not work as well as I thought it would
				//break;
			}

			if (j != 3 && (most3[x1] > up[x1] || most3[x2] > up[x2]))
			{
				for (int j = x1; j <= x2; ++j)
				{
					down[j] = clamp (most3[j], up[j], dwal[j]);
				}
				wallscan (x1, x2, up, down, swal, lwal);

				up = down;
				down = (down == most1) ? most2 : most1;
			}
		}

		if (el->Lights[i].Master == NULL)
		{
			basecolormap = floodcolormap;
			wallshade = floodshade;
			fogginess = floodfoggy;
		}
		else
		{
			basecolormap = el->Lights[i].Master->ColorMap->Maps;
			fogginess = level.fadeto || el->Lights[i].Master->ColorMap->Fade;
			wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess,
				el->Lights[i].Master->lightlevel) + r_actualextralight);
			if (el->Lights[i].bFlooder)
			{
				floodcolormap = basecolormap;
				floodshade = wallshade;
				flooding = true;
			}
		}
	}
	wallscan (x1, x2, up, dwal, swal, lwal);
	basecolormap = startcolormap;
	wallshade = startshade;
}

//
// R_RenderSegLoop
// Draws zero, one, or two textures for walls.
// Can draw or mark the starting pixel of floor and ceiling textures.
// CALLED: CORE LOOPING ROUTINE.
//
// [RH] Rewrote this to use Build's wallscan, so it's quite far
// removed from the original Doom routine.
//

void R_RenderSegLoop ()
{
	int x1 = rw_x;
	int x2 = rw_stopx;
	int x;
	int xscale;
	fixed_t xoffset = rw_offset;

	if (fixedlightlev)
		dc_colormap = basecolormap + fixedlightlev;
	else if (fixedcolormap)
		dc_colormap = fixedcolormap;

	// clip wall to the floor and ceiling
	for (x = x1; x < x2; ++x)
	{
		if (walltop[x] < ceilingclip[x])
		{
			walltop[x] = ceilingclip[x];
		}
		if (wallbottom[x] > floorclip[x])
		{
			wallbottom[x] = floorclip[x];
		}
	}

	// mark ceiling areas
	if (markceiling)
	{
		for (x = x1; x < x2; ++x)
		{
			short top = ceilingclip[x];
			short bottom = MIN (walltop[x], floorclip[x]);
			if (top < bottom)
			{
				ceilingplane->top[x] = top;
				ceilingplane->bottom[x] = bottom;
			}
		}
	}

	// mark floor areas
	if (markfloor)
	{
		for (x = x1; x < x2; ++x)
		{
			short top = MAX (wallbottom[x], ceilingclip[x]);
			short bottom = floorclip[x];
			if (top < bottom)
			{
				floorplane->top[x] = top;
				floorplane->bottom[x] = bottom;
			}
		}
	}

	// draw the wall tiers
	if (midtexture)
	{ // one sided line
		if (midtexture->UseType != FTexture::TEX_Null && viewactive)
		{
			dc_texturemid = rw_midtexturemid;
			rw_pic = midtexture;
			xscale = rw_pic->ScaleX ? rw_pic->ScaleX : tx;
			if (xscale != lwallscale)
			{
				PrepLWall (lwall, (curline->sidedef->TexelLength*xscale) << (FRACBITS-3));
				lwallscale = xscale;
			}
			if (midtexture->bWorldPanning)
			{
				rw_offset = MulScale3 (xoffset, midtexture->ScaleX ? midtexture->ScaleX : tx);
			}
			if (fixedlightlev || fixedcolormap || !frontsector->ExtraLights)
			{
				wallscan (x1, x2-1, walltop, wallbottom, swall, lwall);
			}
			else
			{
				wallscan_striped (x1, x2-1, walltop, wallbottom, swall, lwall);
			}
		}
		clearbufshort (ceilingclip+x1, x2-x1, viewheight);
		clearbufshort (floorclip+x1, x2-x1, 0xffff);
	}
	else
	{ // two sided line
		if (toptexture != NULL && toptexture->UseType != FTexture::TEX_Null)
		{ // top wall
			for (x = x1; x < x2; ++x)
			{
				wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]);
			}
			if (viewactive)
			{
				dc_texturemid = rw_toptexturemid;
				rw_pic = toptexture;
				xscale = rw_pic->ScaleX ? rw_pic->ScaleX : tx;
				if (xscale != lwallscale)
				{
					PrepLWall (lwall, (curline->sidedef->TexelLength*xscale) << (FRACBITS-3));
					lwallscale = xscale;
				}
				if (toptexture->bWorldPanning)
				{
					rw_offset = MulScale3 (xoffset, toptexture->ScaleX ? toptexture->ScaleX : tx);
				}
				if (fixedlightlev || fixedcolormap || !frontsector->ExtraLights)
				{
					wallscan (x1, x2-1, walltop, wallupper, swall, lwall);
				}
				else
				{
					wallscan_striped (x1, x2-1, walltop, wallupper, swall, lwall);
				}
			}
			memcpy (ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short));
		}
		else if (markceiling)
		{ // no top wall
			memcpy (ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short));
		}

		
		if (bottomtexture != NULL && bottomtexture->UseType != FTexture::TEX_Null)
		{ // bottom wall
			for (x = x1; x < x2; ++x)
			{
				walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]);
			}
			if (viewactive)
			{
				dc_texturemid = rw_bottomtexturemid;
				rw_pic = bottomtexture;
				xscale = rw_pic->ScaleX ? rw_pic->ScaleX : tx;
				if (xscale != lwallscale)
				{
					PrepLWall (lwall, (curline->sidedef->TexelLength*xscale) << (FRACBITS-3));
					lwallscale = xscale;
				}
				if (bottomtexture->bWorldPanning)
				{
					rw_offset = MulScale3 (xoffset, bottomtexture->ScaleX ? bottomtexture->ScaleX : tx);
				}
				else
				{
					rw_offset = xoffset;
				}
				if (fixedlightlev || fixedcolormap || !frontsector->ExtraLights)
				{
					wallscan (x1, x2-1, walllower, wallbottom, swall, lwall);
				}
				else
				{
					wallscan_striped (x1, x2-1, walllower, wallbottom, swall, lwall);
				}
			}
			memcpy (floorclip+x1, walllower+x1, (x2-x1)*sizeof(short));
		}
		else if (markfloor)
		{ // no bottom wall
			memcpy (floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short));
		}
	}
	rw_offset = xoffset;
}

void R_NewWall ()
{
	fixed_t rowoffset;

	rw_markmirror = false;

	sidedef = curline->sidedef;
	linedef = curline->linedef;

	// mark the segment as visible for auto map
	linedef->flags |= ML_MAPPED;

	midtexture = toptexture = bottomtexture = 0;

	if (backsector == NULL)
	{
		// single sided line
		// a single sided line is terminal, so it must mark ends
		markfloor = markceiling = true;
		// [RH] Render mirrors later, but mark them now.
		if (linedef->special != Line_Mirror || !r_drawmirrors)
		{
			// [RH] Horizon lines do not need to be textured
			if (linedef->special != Line_Horizon)
			{
				midtexture = TexMan(sidedef->midtexture);
				rowoffset = sidedef->rowoffset;
				if (rowoffset < 0 && midtexture != NULL)
				{
					rowoffset += midtexture->GetHeight() << FRACBITS;
				}
				if (linedef->flags & ML_DONTPEGBOTTOM)
				{ // bottom of texture at bottom
					rw_midtexturemid = frontsector->floortexz + (midtexture->GetHeight() << FRACBITS);
				}
				else
				{ // top of texture at top
					rw_midtexturemid = frontsector->ceilingtexz;
				}
				if (midtexture->bWorldPanning)
				{
					rw_midtexturemid = MulScale3 (rw_midtexturemid - viewz + rowoffset,
						midtexture->ScaleY ? midtexture->ScaleY : ty);
				}
				else
				{
					// rowoffset is added outside the multiply so that it positions the texture
					// by texels instead of world units.
					rw_midtexturemid = MulScale3 (rw_midtexturemid - viewz,
						midtexture->ScaleY ? midtexture->ScaleY : ty)
						+ rowoffset;
				}
			}
		}
		else
		{
			rw_markmirror = true;
		}
	}
	else
	{ // two-sided line
		// hack to allow height changes in outdoor areas

		rw_frontlowertop = frontsector->ceilingtexz;

		if (frontsector->ceilingpic == skyflatnum &&
			backsector->ceilingpic == skyflatnum)
		{
			if (rw_havehigh)
			{ // front ceiling is above back ceiling
				memcpy (&walltop[WallSX1], &wallupper[WallSX1], (WallSX2 - WallSX1)*sizeof(walltop[0]));
				rw_havehigh = false;
			}
			else
			{ // back ceiling is above front ceiling
				// Recalculate walltop so that the wall is clipped by the back sector's
				// ceiling instead of the front sector's ceiling.
				WallMost (walltop, backsector->ceilingplane);
			}
			// Putting sky ceilins on the front and back of a line alters the way unpegged
			// positioning works.
			rw_frontlowertop = backsector->ceilingtexz;
		}

		if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) ||
			(rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2))
		{
			// closed door
			markceiling = markfloor = true;
		}
		else
		{
			markfloor = rw_mustmarkfloor
				|| backsector->floorplane != frontsector->floorplane
				|| backsector->lightlevel != frontsector->lightlevel
				|| backsector->floorpic != frontsector->floorpic

				// killough 3/7/98: Add checks for (x,y) offsets
				|| backsector->floor_xoffs != frontsector->floor_xoffs
				|| (backsector->floor_yoffs + backsector->base_floor_yoffs) != (frontsector->floor_yoffs + frontsector->base_floor_yoffs)

				// killough 4/15/98: prevent 2s normals
				// from bleeding through deep water
				|| frontsector->heightsec

				|| backsector->FloorLight != frontsector->FloorLight
				|| backsector->FloorFlags != frontsector->FloorFlags

				// [RH] Add checks for colormaps
				|| backsector->ColorMap != frontsector->ColorMap

				|| backsector->floor_xscale != frontsector->floor_xscale
				|| backsector->floor_yscale != frontsector->floor_yscale

				|| (backsector->floor_angle + backsector->base_floor_angle) != (frontsector->floor_angle + frontsector->base_floor_angle)

				|| (sidedef->midtexture && linedef->flags & ML_CLIP_MIDTEX)
				;

			markceiling = (frontsector->ceilingpic != skyflatnum ||
				backsector->ceilingpic != skyflatnum) &&
				(rw_mustmarkceiling
				|| backsector->ceilingplane != frontsector->ceilingplane
				|| backsector->lightlevel != frontsector->lightlevel
				|| backsector->ceilingpic != frontsector->ceilingpic

				// killough 3/7/98: Add checks for (x,y) offsets
				|| backsector->ceiling_xoffs != frontsector->ceiling_xoffs
				|| (backsector->ceiling_yoffs + backsector->base_ceiling_yoffs) != (frontsector->ceiling_yoffs + frontsector->base_ceiling_yoffs)

				// killough 4/15/98: prevent 2s normals
				// from bleeding through fake ceilings
				|| (frontsector->heightsec && frontsector->ceilingpic != skyflatnum)

				|| backsector->CeilingLight != frontsector->CeilingLight
				|| backsector->CeilingFlags != frontsector->CeilingFlags

				// [RH] Add check for colormaps
				|| backsector->ColorMap != frontsector->ColorMap

				|| backsector->ceiling_xscale != frontsector->ceiling_xscale
				|| backsector->ceiling_yscale != frontsector->ceiling_yscale

				|| (backsector->ceiling_angle + backsector->base_ceiling_angle) != (frontsector->ceiling_angle + frontsector->base_ceiling_angle)

				|| (sidedef->midtexture && linedef->flags & ML_CLIP_MIDTEX)
				);
		}

		if (rw_havehigh)
		{ // top texture
			toptexture = TexMan(sidedef->toptexture);
			const int scale = toptexture->ScaleY ? toptexture->ScaleY : ty;

			if (linedef->flags & ML_DONTPEGTOP)
			{ // top of texture at top
				rw_toptexturemid = MulScale3 (frontsector->ceilingtexz - viewz, scale);
			}
			else
			{ // bottom of texture at bottom
				rw_toptexturemid = MulScale3 (backsector->ceilingtexz - viewz, scale) + (toptexture->GetHeight() << FRACBITS);
			}
			rowoffset = sidedef->rowoffset;
			if (rowoffset < 0 && toptexture != NULL)
			{
				rowoffset += toptexture->GetHeight() << FRACBITS;
			}
			if (toptexture->bWorldPanning)
			{
				rw_toptexturemid += MulScale3 (rowoffset, scale);
			}
			else
			{
				rw_toptexturemid += rowoffset;
			}
		}
		if (rw_havelow)
		{ // bottom texture
			bottomtexture = TexMan(sidedef->bottomtexture);

			if (linedef->flags & ML_DONTPEGBOTTOM)
			{ // bottom of texture at bottom
				rw_bottomtexturemid = rw_frontlowertop;
			}
			else
			{ // top of texture at top
				rw_bottomtexturemid = backsector->floortexz;
			}
			rowoffset = sidedef->rowoffset;
			if (rowoffset < 0 && bottomtexture != NULL)
			{
				rowoffset += bottomtexture->GetHeight() << FRACBITS;
			}
			if (bottomtexture->bWorldPanning)
			{
				rw_bottomtexturemid = MulScale3 (rw_bottomtexturemid - viewz + rowoffset,
					bottomtexture->ScaleY ? bottomtexture->ScaleY : ty);
			}
			else
			{
				rw_bottomtexturemid = MulScale3 (rw_bottomtexturemid - viewz,
					bottomtexture->ScaleY ? bottomtexture->ScaleY : ty)
					+ rowoffset;
			}
		}
	}

	// if a floor / ceiling plane is on the wrong side of the view plane,
	// it is definitely invisible and doesn't need to be marked.

	// killough 3/7/98: add deep water check
	if (frontsector->heightsec == NULL ||
		(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))
	{
		if (frontsector->floorplane.ZatPoint (viewx, viewy) >= viewz)       // above view plane
			markfloor = false;
		if (frontsector->ceilingplane.ZatPoint (viewx, viewy) <= viewz &&
			frontsector->ceilingpic != skyflatnum)   // below view plane
			markceiling = false;
	}

	segtextured = TexMan(sidedef->midtexture) != NULL || toptexture != NULL || bottomtexture != NULL;

	// calculate light table
	if (segtextured || (backsector && IsFogBoundary (frontsector, backsector)))
	{
		lwallscale = TexMan(sidedef->midtexture) ? TexMan(sidedef->midtexture)->ScaleX :
							toptexture ? toptexture->ScaleX :
							bottomtexture ? bottomtexture->ScaleX : 0;
		if (lwallscale == 0)
		{
			lwallscale = tx;
		}
		PrepWall (swall, lwall, (sidedef->TexelLength*lwallscale) << (FRACBITS-3));

		if (!fixedcolormap)
		{
			wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, frontsector->lightlevel)
				+ r_actualextralight);
			GlobVis = r_WallVisibility;
			rw_lightleft = SafeDivScale12 (GlobVis, WallSZ1);
			rw_lightstep = (SafeDivScale12 (GlobVis, WallSZ2) - rw_lightleft) / (WallSX2 - WallSX1);
		}
		else
		{
			rw_lightleft = FRACUNIT;
			rw_lightstep = 0;
		}
	}
}

int side_s::GetLightLevel (bool foggy, int baselight) const
{
	// [RH] Get wall light level
	if (this->Flags & WALLF_ABSLIGHTING)
	{
		return (BYTE)this->Light;
	}
	else
	{
		if (!foggy) // Don't do relative lighting in foggy sectors
		{
			baselight += this->Light * 2;
		}
		return baselight;
	}
}


//
// R_CheckDrawSegs
//

void R_CheckDrawSegs ()
{
	if (ds_p == &drawsegs[MaxDrawSegs])
	{ // [RH] Grab some more drawsegs
		size_t newdrawsegs = MaxDrawSegs ? MaxDrawSegs*2 : 32;
		ptrdiff_t firstofs = firstdrawseg - drawsegs;
		drawsegs = (drawseg_t *)Realloc (drawsegs, newdrawsegs * sizeof(drawseg_t));
		firstdrawseg = drawsegs + firstofs;
		ds_p = drawsegs + MaxDrawSegs;
		MaxDrawSegs = newdrawsegs;
		DPrintf ("MaxDrawSegs increased to %d\n", MaxDrawSegs);
	}
}

//
// R_CheckOpenings
//

void R_CheckOpenings (size_t need)
{	
	need += lastopening;

	if (need > maxopenings)
	{
		do
			maxopenings = maxopenings ? maxopenings*2 : 16384;
		while (need > maxopenings);
		openings = (short *)Realloc (openings, maxopenings * sizeof(*openings));
		DPrintf ("MaxOpenings increased to %u\n", maxopenings);
	}
}

//
// R_StoreWallRange
// A wall segment will be drawn between start and stop pixels (inclusive).
//

void R_StoreWallRange (int start, int stop)
{
	bool maskedtexture = false;

#ifdef RANGECHECK
	if (start >= viewwidth || start >= stop)
		I_FatalError ("Bad R_StoreWallRange: %i to %i", start , stop);
#endif

	// don't overflow and crash
	R_CheckDrawSegs ();
	
	if (!rw_prepped)
	{
		rw_prepped = true;
		R_NewWall ();
	}

	rw_offset = sidedef->textureoffset;
	rw_light = rw_lightleft + rw_lightstep * (start - WallSX1);

	ds_p->sx1 = WallSX1;
	ds_p->sx2 = WallSX2;
	ds_p->sz1 = WallSZ1;
	ds_p->sz2 = WallSZ2;
	ds_p->siz1 = (DWORD)DivScale32 (1, WallSZ1) >> 1;
	ds_p->siz2 = (DWORD)DivScale32 (1, WallSZ2) >> 1;
	ds_p->x1 = rw_x = start;
	ds_p->x2 = stop-1;
	ds_p->curline = curline;
	rw_stopx = stop;
	ds_p->bFogBoundary = false;

	// killough 1/6/98, 2/1/98: remove limit on openings
	R_CheckOpenings ((stop - start)*6);

	ds_p->sprtopclip = ds_p->sprbottomclip = ds_p->maskedtexturecol = ds_p->swall = -1;

	if (rw_markmirror)
	{
		WallMirrors.Push (ds_p - drawsegs);
		ds_p->silhouette = SIL_BOTH;
	}
	else if (backsector == NULL)
	{
		ds_p->sprtopclip = R_NewOpening (stop - start);
		ds_p->sprbottomclip = R_NewOpening (stop - start);
		clearbufshort (openings + ds_p->sprtopclip, stop-start, viewheight);
		memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short));
		ds_p->silhouette = SIL_BOTH;
	}
	else
	{
		// two sided line
		ds_p->silhouette = 0;

		if (rw_frontfz1 > rw_backfz1 || rw_frontfz2 > rw_backfz2 ||
			backsector->floorplane.ZatPoint (viewx, viewy) > viewz)
		{
			ds_p->silhouette = SIL_BOTTOM;
		}

		if (rw_frontcz1 < rw_backcz1 || rw_frontcz2 < rw_backcz2 ||
			backsector->ceilingplane.ZatPoint (viewx, viewy) < viewz)
		{
			ds_p->silhouette |= SIL_TOP;
		}

		// killough 1/17/98: this test is required if the fix
		// for the automap bug (r_bsp.c) is used, or else some
		// sprites will be displayed behind closed doors. That
		// fix prevents lines behind closed doors with dropoffs
		// from being displayed on the automap.
		//
		// killough 4/7/98: make doorclosed external variable

		{
			extern int doorclosed;	// killough 1/17/98, 2/8/98, 4/7/98
			if (doorclosed || (rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2))
			{
				ds_p->sprbottomclip = R_NewOpening (stop - start);
				memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short));
				ds_p->silhouette |= SIL_BOTTOM;
			}
			if (doorclosed || (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2))
			{						// killough 1/17/98, 2/8/98
				ds_p->sprtopclip = R_NewOpening (stop - start);
				clearbufshort (openings + ds_p->sprtopclip, stop - start, viewheight);
				ds_p->silhouette |= SIL_TOP;
			}
		}

		// allocate space for masked texture tables, if needed
		// [RH] Don't just allocate the space; fill it in too.
		if ((TexMan(sidedef->midtexture)->UseType != FTexture::TEX_Null || IsFogBoundary (frontsector, backsector)) &&
			(rw_ceilstat != 12 || sidedef->toptexture == 0) &&
			(rw_floorstat != 3 || sidedef->bottomtexture == 0) &&
			(WallSZ1 >= TOO_CLOSE_Z && WallSZ2 >= TOO_CLOSE_Z))
		{
			fixed_t *swal;
			short *lwal;
			int i;

			maskedtexture = true;

			ds_p->bFogBoundary = IsFogBoundary (frontsector, backsector);
			if (sidedef->midtexture != 0)
			{
				ds_p->maskedtexturecol = R_NewOpening (stop - start);
				ds_p->swall = R_NewOpening ((stop - start) * 2);

				lwal = openings + ds_p->maskedtexturecol;
				swal = (fixed_t *)(openings + ds_p->swall);
				int scaley = TexMan(sidedef->midtexture)->ScaleY ?
					TexMan(sidedef->midtexture)->ScaleY : ty;
				int xoffset = rw_offset;
				for (i = start; i < stop; i++)
				{
					*lwal++ = (short)((lwall[i] + xoffset) >> FRACBITS);
					*swal++ = MulScale5 (swall[i], scaley);
				}

				fixed_t istart = *((fixed_t *)(openings + ds_p->swall));
				fixed_t iend = *(swal - 1);

				if (istart < 3 && istart >= 0) istart = 3;
				if (istart > -3 && istart < 0) istart = -3;
				if (iend < 3 && iend >= 0) iend = 3;
				if (iend > -3 && iend < 0) iend = -3;
				istart = DivScale32 (1, istart);
				iend = DivScale32 (1, iend);
				ds_p->iscale = istart;

				if (stop - start > 1)
				{
					ds_p->iscalestep = (iend - istart) / (stop - start - 1);
				}
				else
				{
					ds_p->iscalestep = 0;
				}
			}
			ds_p->light = rw_light;
			ds_p->lightstep = rw_lightstep;
			ds_p->shade = wallshade;

			if (ds_p->bFogBoundary || ds_p->maskedtexturecol != -1)
			{
				InterestingDrawsegs.Push (ds_p - drawsegs);
			}
		}
	}
	
	// render it
	if (markceiling)
	{
		if (ceilingplane)
		{	// killough 4/11/98: add NULL ptr checks
			ceilingplane = R_CheckPlane (ceilingplane, start, stop-1);
		}
		else
		{
			markceiling = false;
		}
	}
	
	if (markfloor)
	{
		if (floorplane)
		{	// killough 4/11/98: add NULL ptr checks
			floorplane = R_CheckPlane (floorplane, start, stop-1);
		}
		else
		{
			markfloor = false;
		}
	}

	R_RenderSegLoop ();

	// save sprite clipping info
	if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && ds_p->sprtopclip == -1)
	{
		ds_p->sprtopclip = R_NewOpening (stop - start);
		memcpy (openings + ds_p->sprtopclip, &ceilingclip[start], sizeof(short)*(stop-start));
	}

	if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && ds_p->sprbottomclip == -1)
	{
		ds_p->sprbottomclip = R_NewOpening (stop - start);
		memcpy (openings + ds_p->sprbottomclip, &floorclip[start], sizeof(short)*(stop-start));
	}

	if (maskedtexture && curline->sidedef->midtexture != 0)
	{
		ds_p->silhouette |= SIL_TOP | SIL_BOTTOM;
	}

	// [RH] Draw any decals bound to the seg
	AActor *decal = (AActor *)curline->sidedef->BoundActors;
	while (decal != NULL)
	{
		R_RenderBoundWallSprite (decal, ds_p, 0);
		decal = decal->snext;
	}

	ds_p++;
}

int OWallMost (short *mostbuf, fixed_t z)
{
	int bad, y, ix1, ix2, iy1, iy2;
	fixed_t s1, s2, s3, s4;

	z = -(z >> 4);
	s1 = MulScale16 (globaluclip, WallSZ1); s2 = MulScale16 (globaluclip, WallSZ2);
	s3 = MulScale16 (globaldclip, WallSZ1); s4 = MulScale16 (globaldclip, WallSZ2);
	bad = (z<s1)+((z<s2)<<1)+((z>s3)<<2)+((z>s4)<<3);

	if ((bad&3) == 3)
	{
		memset (&mostbuf[WallSX1], 0, (WallSX2 - WallSX1)*sizeof(mostbuf[0]));
		return bad;
	}

	if ((bad&12) == 12)
	{
		clearbufshort (&mostbuf[WallSX1], WallSX2 - WallSX1, viewheight);
		return bad;
	}

	ix1 = WallSX1; iy1 = WallSZ1;
	ix2 = WallSX2; iy2 = WallSZ2;

	if (bad & 3)
	{
		int t = DivScale30 (z-s1, s2-s1);
		int inty = WallSZ1 + MulScale30 (WallSZ2 - WallSZ1, t);
		int xcross = WallSX1 + Scale (MulScale30 (WallSZ2, t), WallSX2 - WallSX1, inty);

		if ((bad & 3) == 2)
		{
			if (WallSX1 <= xcross) { iy2 = inty; ix2 = xcross; }
			if (WallSX2 > xcross) memset (&mostbuf[xcross], 0, (WallSX2-xcross)*sizeof(mostbuf[0]));
		}
		else
		{
			if (xcross <= WallSX2) { iy1 = inty; ix1 = xcross; }
			if (xcross > WallSX1) memset (&mostbuf[WallSX1], 0, (xcross-WallSX1)*sizeof(mostbuf[0]));
		}
	}

	if (bad & 12)
	{
		int t = DivScale30 (z-s3, s4-s3);
		int inty = WallSZ1 + MulScale30 (WallSZ2 - WallSZ1, t);
		int xcross = WallSX1 + Scale (MulScale30 (WallSZ2, t), WallSX2 - WallSX1, inty);

		if ((bad & 12) == 8)
		{
			if (WallSX1 <= xcross) { iy2 = inty; ix2 = xcross; }
			if (WallSX2 > xcross) clearbufshort (&mostbuf[xcross], WallSX2 - xcross, viewheight);
		}
		else
		{
			if (xcross <= WallSX2) { iy1 = inty; ix1 = xcross; }
			if (xcross > WallSX1) clearbufshort (&mostbuf[WallSX1], xcross - WallSX1, viewheight);
		}
	}

	y = Scale (z, InvZtoScale, iy1);
	if (ix2 == ix1)
	{
		mostbuf[ix1] = (short)((y + centeryfrac) >> FRACBITS);
	}
	else
	{
		fixed_t yinc  = (Scale (z, InvZtoScale, iy2) - y) / (ix2 - ix1);
		qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, y + centeryfrac, yinc);
	}

	if (mostbuf[ix1] < 0) mostbuf[ix1] = 0;
	else if (mostbuf[ix1] > viewheight) mostbuf[ix1] = (short)viewheight;
	if (mostbuf[ix2] < 0) mostbuf[ix2] = 0;
	else if (mostbuf[ix2] > viewheight) mostbuf[ix2] = (short)viewheight;

	return bad;
}

int WallMost (short *mostbuf, const secplane_t &plane)
{
	if ((plane.a | plane.b) == 0)
	{
		return OWallMost (mostbuf, ((plane.c < 0) ? plane.d : -plane.d) - viewz);
	}

	fixed_t x, y, den, z1, z2, oz1, oz2;
	fixed_t s1, s2, s3, s4;
	int bad, ix1, ix2, iy1, iy2;

	if (MirrorFlags & RF_XFLIP)
	{
		x = curline->v2->x;
		y = curline->v2->y;
		if (WallSX1 == 0 && 0 != (den = WallTX1 - WallTX2 + WallTY1 - WallTY2))
		{
			int frac = SafeDivScale30 (WallTY1 + WallTX1, den);
			x -= MulScale30 (frac, x - curline->v1->x);
			y -= MulScale30 (frac, y - curline->v1->y);
		}
		z1 = viewz - plane.ZatPoint (x, y);

		if (WallSX2 > WallSX1 + 1)
		{
			x = curline->v1->x;
			y = curline->v1->y;
			if (WallSX2 == viewwidth && 0 != (den = WallTX1 - WallTX2 - WallTY1 + WallTY2))
			{
				int frac = SafeDivScale30 (WallTY2 - WallTX2, den);
				x += MulScale30 (frac, curline->v2->x - x);
				y += MulScale30 (frac, curline->v2->y - y);
			}
			z2 = viewz - plane.ZatPoint (x, y);
		}
		else
		{
			z2 = z1;
		}
	}
	else
	{
		x = curline->v1->x;
		y = curline->v1->y;
		if (WallSX1 == 0 && 0 != (den = WallTX1 - WallTX2 + WallTY1 - WallTY2))
		{
			int frac = SafeDivScale30 (WallTY1 + WallTX1, den);
			x += MulScale30 (frac, curline->v2->x - x);
			y += MulScale30 (frac, curline->v2->y - y);
		}
		z1 = viewz - plane.ZatPoint (x, y);

		if (WallSX2 > WallSX1 + 1)
		{
			x = curline->v2->x;
			y = curline->v2->y;
			if (WallSX2 == viewwidth && 0 != (den = WallTX1 - WallTX2 - WallTY1 + WallTY2))
			{
				int frac = SafeDivScale30 (WallTY2 - WallTX2, den);
				x -= MulScale30 (frac, x - curline->v1->x);
				y -= MulScale30 (frac, y - curline->v1->y);
			}
			z2 = viewz - plane.ZatPoint (x, y);
		}
		else
		{
			z2 = z1;
		}
	}

	s1 = MulScale12 (globaluclip, WallSZ1); s2 = MulScale12 (globaluclip, WallSZ2);
	s3 = MulScale12 (globaldclip, WallSZ1); s4 = MulScale12 (globaldclip, WallSZ2);
	bad = (z1<s1)+((z2<s2)<<1)+((z1>s3)<<2)+((z2>s4)<<3);

	ix1 = WallSX1; ix2 = WallSX2;
	iy1 = WallSZ1; iy2 = WallSZ2;
	oz1 = z1; oz2 = z2;

	if ((bad&3) == 3)
	{
		memset (&mostbuf[ix1], -1, (ix2-ix1)*sizeof(mostbuf[0]));
		return bad;
	}

	if ((bad&12) == 12)
	{
		clearbufshort (&mostbuf[ix1], ix2-ix1, viewheight);
		return bad;
	}

	if (bad&3)
	{
			//inty = intz / (globaluclip>>16)
		int t = SafeDivScale30 (oz1-s1, s2-s1+oz1-oz2);
		int inty = WallSZ1 + MulScale30 (WallSZ2-WallSZ1,t);
		int intz = oz1 + MulScale30 (oz2-oz1,t);
		int xcross = WallSX1 + Scale (MulScale30 (WallSZ2, t), WallSX2-WallSX1, inty);

		//t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4));
		//inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
		//intz = z1 + mulscale30(z2-z1,t);

		if ((bad&3) == 2)
		{
			if (WallSX1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; }
			memset (&mostbuf[xcross], 0, (WallSX2-xcross)*sizeof(mostbuf[0]));
		}
		else
		{
			if (xcross <= WallSX2) { z1 = intz; iy1 = inty; ix1 = xcross; }
			memset (&mostbuf[WallSX1], 0, (xcross-WallSX1)*sizeof(mostbuf[0]));
		}
	}

	if (bad&12)
	{
			//inty = intz / (globaldclip>>16)
		int t = SafeDivScale30 (oz1-s3, s4-s3+oz1-oz2);
		int inty = WallSZ1 + MulScale30 (WallSZ2-WallSZ1,t);
		int intz = oz1 + MulScale30 (oz2-oz1,t);
		int xcross = WallSX1 + Scale (MulScale30 (WallSZ2, t), WallSX2-WallSX1,inty);

		//t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4));
		//inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
		//intz = z1 + mulscale30(z2-z1,t);

		if ((bad&12) == 8)
		{
			if (WallSX1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; }
			if (WallSX2 > xcross) clearbufshort (&mostbuf[xcross], WallSX2-xcross, viewheight);
		}
		else
		{
			if (xcross <= WallSX2) { z1 = intz; iy1 = inty; ix1 = xcross; }
			if (xcross > WallSX1) clearbufshort (&mostbuf[WallSX1], xcross-WallSX1, viewheight);
		}
	}

	y = Scale (z1>>4, InvZtoScale, iy1);
	if (ix2 == ix1)
	{
		mostbuf[ix1] = (short)((y + centeryfrac) >> FRACBITS);
	}
	else
	{
		fixed_t yinc = (Scale (z2>>4, InvZtoScale, iy2) - y) / (ix2-ix1);
		qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, y + centeryfrac,yinc);
	}

	if (mostbuf[ix1] < 0) mostbuf[ix1] = 0;
	else if (mostbuf[ix1] > viewheight) mostbuf[ix1] = (short)viewheight;
	if (mostbuf[ix2] < 0) mostbuf[ix2] = 0;
	else if (mostbuf[ix2] > viewheight) mostbuf[ix2] = (short)viewheight;

	return bad;
}

void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat)
{ // swall = scale, lwall = texturecolumn
	int x;
	float top, bot, i;
	float xrepeat = (float)walxrepeat;
	float ol, l, topinc, botinc;

	i = (float)(WallSX1 - centerx);
	top = WallUoverZorg + WallUoverZstep * i;
	bot = WallInvZorg + WallInvZstep * i;
	topinc = WallUoverZstep * 4.f;
	botinc = WallInvZstep * 4.f;

	x = WallSX1;

	l = top / bot;
	swall[x] = quickertoint (l * WallDepthScale + WallDepthOrg);
	lwall[x] = quickertoint (l * xrepeat);
	// As long as l is invalid, step one column at a time so that
	// we can get as many correct texture columns as possible.
	while (l > 1.0 && x+1 < WallSX2)
	{
		l = (top += WallUoverZstep) / (bot += WallInvZstep);
		x++;
		swall[x] = quickertoint (l * WallDepthScale + WallDepthOrg);
		lwall[x] = quickertoint (l * xrepeat);
	}
	l *= xrepeat;
	while (x+4 < WallSX2)
	{
		top += topinc; bot += botinc;
		ol = l; l = top / bot;
		swall[x+4] = quickertoint (l * WallDepthScale + WallDepthOrg);
		lwall[x+4] = quickertoint (l *= xrepeat);

		i = (ol+l) * 0.5f;
		lwall[x+2] = quickertoint (i);
		lwall[x+1] = quickertoint ((ol+i) * 0.5f);
		lwall[x+3] = quickertoint ((l+i) * 0.5f);
		swall[x+2] = ((swall[x]+swall[x+4])>>1);
		swall[x+1] = ((swall[x]+swall[x+2])>>1);
		swall[x+3] = ((swall[x+4]+swall[x+2])>>1);
		x += 4;
	}
	if (x+2 < WallSX2)
	{
		top += topinc * 0.5f; bot += botinc * 0.5f;
		ol = l; l = top / bot;
		swall[x+2] = quickertoint (l * WallDepthScale + WallDepthOrg);
		lwall[x+2] = quickertoint (l *= xrepeat);

		lwall[x+1] = quickertoint ((l+ol)*0.5f);
		swall[x+1] = (swall[x]+swall[x+2])>>1;
		x += 2;
	}
	if (x+1 < WallSX2)
	{
		l = (top + WallUoverZstep) / (bot + WallInvZstep);
		swall[x+1] = quickertoint (l * WallDepthScale + WallDepthOrg);
		lwall[x+1] = quickertoint (l * xrepeat);
	}
	/*
	for (x = WallSX1; x < WallSX2; x++)
	{
		frac = top / bot;
		lwall[x] = quickertoint (frac * xrepeat);
		swall[x] = quickertoint (frac * WallDepthScale + WallDepthOrg);
		top += WallUoverZstep;
		bot += WallInvZstep;
	}
	*/

	// fix for rounding errors
	walxrepeat -= FRACUNIT;
	fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat : 0;
	if (WallSX1 > 0)
	{
		for (x = WallSX1; x < WallSX2; x++)
		{
			if ((unsigned)lwall[x] > (unsigned)walxrepeat)
			{
				lwall[x] = fix;
			}
			else
			{
				break;
			}
		}
	}
	fix = walxrepeat - fix;
	for (x = WallSX2-1; x >= WallSX1; x--)
	{
		if ((unsigned)lwall[x] > (unsigned)walxrepeat)
		{
			lwall[x] = fix;
		}
		else
		{
			break;
		}
	}
}

void PrepLWall (fixed_t *lwall, fixed_t walxrepeat)
{ // lwall = texturecolumn
	int x;
	float top, bot, i;
	float xrepeat = (float)walxrepeat;
	float ol, l, topinc, botinc;

	i = (float)(WallSX1 - centerx);
	top = WallUoverZorg + WallUoverZstep * i;
	bot = WallInvZorg + WallInvZstep * i;
	topinc = WallUoverZstep * 4.f;
	botinc = WallInvZstep * 4.f;

	x = WallSX1;

	l = top / bot;
	lwall[x] = quickertoint (l * xrepeat);
	// As long as l is invalid, step one column at a time so that
	// we can get as many correct texture columns as possible.
	while (l > 1.0 && x+1 < WallSX2)
	{
		l = (top += WallUoverZstep) / (bot += WallInvZstep);
		lwall[++x] = quickertoint (l * xrepeat);
	}
	l *= xrepeat;
	while (x+4 < WallSX2)
	{
		top += topinc; bot += botinc;
		ol = l; l = top / bot;
		lwall[x+4] = quickertoint (l *= xrepeat);

		i = (ol+l) * 0.5f;
		lwall[x+2] = quickertoint (i);
		lwall[x+1] = quickertoint ((ol+i) * 0.5f);
		lwall[x+3] = quickertoint ((l+i) * 0.5f);
		x += 4;
	}
	if (x+2 < WallSX2)
	{
		top += topinc * 0.5f; bot += botinc * 0.5f;
		ol = l; l = top / bot;
		lwall[x+2] = quickertoint (l *= xrepeat);
		lwall[x+1] = quickertoint ((l+ol)*0.5f);
		x += 2;
	}
	if (x+1 < WallSX2)
	{
		l = (top + WallUoverZstep) / (bot + WallInvZstep);
		lwall[x+1] = quickertoint (l * xrepeat);
	}

	// fix for rounding errors
	walxrepeat -= FRACUNIT;
	fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat : 0;
	if (WallSX1 > 0)
	{
		for (x = WallSX1; x < WallSX2; x++)
		{
			if ((unsigned)lwall[x] > (unsigned)walxrepeat)
			{
				lwall[x] = fix;
			}
			else
			{
				break;
			}
		}
	}
	fix = walxrepeat - fix;
	for (x = WallSX2-1; x >= WallSX1; x--)
	{
		if ((unsigned)lwall[x] > (unsigned)walxrepeat)
		{
			lwall[x] = fix;
		}
		else
		{
			break;
		}
	}
}

// pass = 0: when seg is first drawn
//		= 1: drawing masked textures (including sprites)
// Currently, only pass = 0 is done or used

static AActor *DecalActorForDebug;

static void R_RenderBoundWallSprite (AActor *actor, drawseg_t *clipper, int pass)
{
	fixed_t lx, ly, lx2, ly2;
	int x1, x2;
	int xscale, yscale;
	fixed_t topoff;
	byte flipx;
	fixed_t zpos;
	int needrepeat = 0;
	sector_t *front, *back;

	if (actor->renderflags & RF_INVISIBLE || !viewactive)
		return;

	DecalActorForDebug = actor;

	// Determine actor z
	zpos = actor->z;
	front = curline->frontsector;
	back = (curline->backsector != NULL) ? curline->backsector : curline->frontsector;
	switch (actor->renderflags & RF_RELMASK)
	{
	default:
		zpos = actor->z;
		break;
	case RF_RELUPPER:
		if (curline->linedef->flags & ML_DONTPEGTOP)
		{
			zpos = actor->z + front->ceilingtexz;
		}
		else
		{
			zpos = actor->z + back->ceilingtexz;
		}
		break;
	case RF_RELLOWER:
		if (curline->linedef->flags & ML_DONTPEGBOTTOM)
		{
			zpos = actor->z + front->ceilingtexz;
		}
		else
		{
			zpos = actor->z + back->floortexz;
		}
		break;
	case RF_RELMID:
		if (curline->linedef->flags & ML_DONTPEGBOTTOM)
		{
			zpos = actor->z + front->floortexz;
		}
		else
		{
			zpos = actor->z + front->ceilingtexz;
		}
	}

	xscale = actor->xscale + 1;
	yscale = actor->yscale + 1;

	if (actor->picnum != 0xffff)
	{
		WallSpriteTile = TexMan(actor->picnum);
		flipx = actor->renderflags & RF_XFLIP;
	}
	else
	{
		//WallSpriteTile = SpriteFrames[sprites[actor->sprite].spriteframes + actor->frame].lump[0];
		//flipx = SpriteFrames[sprites[actor->sprite].spriteframes + actor->frame].flip & 1;
		return;
	}
	if (WallSpriteTile->UseType == FTexture::TEX_Null)
	{
		return;
	}

	// Determine left and right edges of sprite. Since this sprite is bound
	// to a wall, we use the wall's angle instead of the actor's. This is
	// pretty much the same as what R_AddLine() does.

	x2 = WallSpriteTile->GetWidth();
	x1 = WallSpriteTile->LeftOffset;
	x2 = x2 - x1;

	x1 *= xscale;
	x2 *= xscale;

	angle_t ang = R_PointToAngle2 (curline->v1->x, curline->v1->y, curline->v2->x, curline->v2->y) >> ANGLETOFINESHIFT;
	lx  = actor->x - MulScale6 (x1, finecosine[ang]) - viewx;
	lx2 = actor->x + MulScale6 (x2, finecosine[ang]) - viewx;
	ly  = actor->y - MulScale6 (x1, finesine[ang]) - viewy;
	ly2 = actor->y + MulScale6 (x2, finesine[ang]) - viewy;

	WallTX1 = DMulScale20 (lx,  viewsin, -ly,  viewcos);
	WallTX2 = DMulScale20 (lx2, viewsin, -ly2, viewcos);

	WallTY1 = DMulScale20 (lx, viewtancos,  ly, viewtansin);
	WallTY2 = DMulScale20 (lx2, viewtancos, ly2, viewtansin);

	if (MirrorFlags & RF_XFLIP)
	{
		int t = 256-WallTX1;
		WallTX1 = 256-WallTX2;
		WallTX2 = t;
		swap (WallTY1, WallTY2);
	}

	if (WallTX1 >= -WallTY1)
	{
		if (WallTX1 > WallTY1) return;	// left edge is off the right side
		if (WallTY1 == 0) return;
		x1 = (centerxfrac + Scale (WallTX1, centerxfrac, WallTY1)) >> FRACBITS;
		if (WallTX1 >= 0) x1 = MIN (viewwidth, x1+1); // fix for signed divide
		WallSZ1 = WallTY1;
	}
	else
	{
		if (WallTX2 < -WallTY2) return;	// wall is off the left side
		fixed_t den = WallTX1 - WallTX2 - WallTY2 + WallTY1;	
		if (den == 0) return;
		x1 = 0;
		WallSZ1 = WallTY1 + Scale (WallTY2 - WallTY1, WallTX1 + WallTY1, den);
	}

	if (WallSZ1 < TOO_CLOSE_Z)
		return;

	if (WallTX2 <= WallTY2)
	{
		if (WallTX2 < -WallTY2) return;	// right edge is off the left side
		if (WallTY2 == 0) return;
		x2 = (centerxfrac + Scale (WallTX2, centerxfrac, WallTY2)) >> FRACBITS;
		if (WallTX2 >= 0) x2 = MIN (viewwidth, x2+1);	// fix for signed divide
		WallSZ2 = WallTY2;
	}
	else
	{
		if (WallTX1 > WallTY1) return;	// wall is off the right side
		fixed_t den = WallTY2 - WallTY1 - WallTX2 + WallTX1;
		if (den == 0) return;
		x2 = viewwidth;
		WallSZ2 = WallTY1 + Scale (WallTY2 - WallTY1, WallTX1 - WallTY1, den);
	}

	if (x1 >= x2 || x1 > clipper->x2 || x2 <= clipper->x1 || WallSZ2 < TOO_CLOSE_Z)
		return;

	if (MirrorFlags & RF_XFLIP)
	{
		WallUoverZorg = (float)WallTX2 * WallTMapScale;
		WallUoverZstep = (float)(-WallTY2) * 32.f;
		WallInvZorg = (float)(WallTX2 - WallTX1) * WallTMapScale;
		WallInvZstep = (float)(WallTY1 - WallTY2) * 32.f;
	}
	else
	{
		WallUoverZorg = (float)WallTX1 * WallTMapScale;
		WallUoverZstep = (float)(-WallTY1) * 32.f;
		WallInvZorg = (float)(WallTX1 - WallTX2) * WallTMapScale;
		WallInvZstep = (float)(WallTY2 - WallTY1) * 32.f;
	}
	WallDepthScale = WallInvZstep * WallTMapScale2;
	WallDepthOrg = -WallUoverZstep * WallTMapScale2;

	// Get the top and bottom clipping arrays
	switch (actor->renderflags & RF_CLIPMASK)
	{
	case RF_CLIPFULL:
		if (curline->backsector == NULL)
		{
			if (pass != 0)
			{
				return;
			}
			mceilingclip = walltop;
			mfloorclip = wallbottom;
		}
		else if (pass == 0)
		{
			mceilingclip = walltop;
			mfloorclip = ceilingclip;
			needrepeat = 1;
		}
		else
		{
			mceilingclip = openings + clipper->sprtopclip - clipper->x1;
			mfloorclip = openings + clipper->sprbottomclip - clipper->x1;
		}
		break;

	case RF_CLIPUPPER:
		if (pass != 0)
		{
			return;
		}
		mceilingclip = walltop;
		mfloorclip = ceilingclip;
		break;

	case RF_CLIPMID:
		if (curline->backsector != NULL && pass != 2)
		{
			return;
		}
		mceilingclip = openings + clipper->sprtopclip - clipper->x1;
		mfloorclip = openings + clipper->sprbottomclip - clipper->x1;
		break;

	case RF_CLIPLOWER:
		if (pass != 0)
		{
			return;
		}
		mceilingclip = floorclip;
		mfloorclip = wallbottom;
		break;
	}

	topoff = WallSpriteTile->TopOffset << FRACBITS;
	dc_texturemid = topoff + SafeDivScale6 (zpos - viewz, yscale);

	// Scale the texture size
	rw_scalestep = MulScale6 (rw_scalestep, yscale);

	// Clip sprite to drawseg
	if (x1 < clipper->x1)
	{
		x1 = clipper->x1;
	}
	if (x2 > clipper->x2)
	{
		x2 = clipper->x2 + 1;
	}
	if (x1 >= x2)
	{
		return;
	}

	swap (x1, WallSX1);
	swap (x2, WallSX2);
	PrepWall (swall, lwall, WallSpriteTile->GetWidth() << FRACBITS);
	swap (x1, WallSX1);
	swap (x2, WallSX2);

	if (flipx)
	{
		int i;
		int right = (WallSpriteTile->GetWidth() << FRACBITS) - 1;

		for (i = x1; i < x2; i++)
		{
			lwall[i] = right - lwall[i];
		}
	}

	// Prepare lighting
	bool calclighting = false;

	rw_light = rw_lightleft + (x1 - WallSX1) * rw_lightstep;
	if (fixedlightlev)
		dc_colormap = basecolormap + fixedlightlev;
	else if (fixedcolormap)
		dc_colormap = fixedcolormap;
	else if (!foggy && (actor->renderflags & RF_FULLBRIGHT))
		dc_colormap = basecolormap;
	else
		calclighting = true;

	// Draw it
	if (actor->renderflags & RF_YFLIP)
	{
		sprflipvert = true;
		yscale = -yscale;
		dc_texturemid = dc_texturemid - (WallSpriteTile->GetHeight() << FRACBITS);
	}
	else
	{
		sprflipvert = false;
	}

	rw_offset = 16*FRACUNIT/yscale;

	do
	{
		dc_x = x1;
		ESPSResult mode;

		mode = R_SetPatchStyle (actor->RenderStyle, actor->alpha, actor->Translation, actor->alphacolor);

		if (mode == DontDraw)
		{
			needrepeat = 0;
		}
		else
		{
			int stop4;

			if (mode == DoDraw0)
			{ // 1 column at a time
				stop4 = dc_x;
			}
			else	 // DoDraw1
			{ // up to 4 columns at a time
				stop4 = x2 & ~3;
			}

			while ((dc_x < stop4) && (dc_x & 3))
			{
				if (calclighting)
				{ // calculate lighting
					dc_colormap = basecolormap + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT);
				}

				WallSpriteColumn (R_DrawMaskedColumn);
				dc_x++;
			}

			while (dc_x < stop4)
			{
				if (calclighting)
				{ // calculate lighting
					dc_colormap = basecolormap + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT);
				}
				rt_initcols();
				for (int zz = 4; zz; --zz)
				{
					WallSpriteColumn (R_DrawMaskedColumnHoriz);
					dc_x++;
				}
				rt_draw4cols (dc_x - 4);
			}

			while (dc_x < x2)
			{
				if (calclighting)
				{ // calculate lighting
					dc_colormap = basecolormap + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT);
				}

				WallSpriteColumn (R_DrawMaskedColumn);
				dc_x++;
			}
		}

		// If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will
		// be set 1 if we need to draw on the lower wall. In all other cases,
		// needrepeat will be 0, and the while will fail.
		mceilingclip = floorclip;
		mfloorclip = wallbottom;
		R_FinishSetPatchStyle ();
	} while (needrepeat--);

	colfunc = basecolfunc;
	hcolfunc_post1 = rt_map1col;
	hcolfunc_post4 = rt_map4cols;

	R_FinishSetPatchStyle ();
}

static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans))
{
	unsigned int texturecolumn = lwall[dc_x] >> FRACBITS;
#if 0
	if (texturecolumn >= (unsigned)WallSpriteTile->GetWidth())
	{
		I_FatalError ("WallSpriteColumn tried to draw part of a decal that didn't exist.\n"
			"Please report all of the following information, because\n"
			"I want to get this fixed, yet I just can't get it happen to me:\n\n"
			"%d->%d/%d"
			"  %s  %04x  %d\n"
			"%d  %d  %d\n"
			"%d  %d  %d  %u\n",
			dc_x, texturecolumn, TileCache[WallSpriteTile]->width,
			WallSpriteTile->Name, DecalActorForDebug->renderflags, DecalActorForDebug->RenderStyle,
			DecalActorForDebug->x, DecalActorForDebug->y, DecalActorForDebug->z,
			viewx, viewy, viewz, viewangle);
		texturecolumn = TileCache[WallSpriteTile]->width-1;
	}
#endif
	dc_iscale = MulScale16 (swall[dc_x], rw_offset);
	spryscale = SafeDivScale32 (1, dc_iscale);
	if (sprflipvert)
		sprtopscreen = centeryfrac + FixedMul (dc_texturemid, spryscale);
	else
		sprtopscreen = centeryfrac - FixedMul (dc_texturemid, spryscale);

	const BYTE *column;
	const FTexture::Span *spans;
	column = WallSpriteTile->GetColumn (texturecolumn, &spans);
	drawfunc (column, spans);
	rw_light += rw_lightstep;
}
