2017-01-03 06:17:54 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2017-01-01 09:28:35 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "templates.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "doomdata.h"
|
|
|
|
#include "p_lnspec.h"
|
2017-01-03 06:13:40 +00:00
|
|
|
#include "p_setup.h"
|
2017-01-01 09:28:35 +00:00
|
|
|
#include "r_sky.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "m_swap.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "stats.h"
|
|
|
|
#include "a_sharedglobal.h"
|
|
|
|
#include "d_net.h"
|
|
|
|
#include "g_level.h"
|
2017-01-16 05:03:21 +00:00
|
|
|
#include "g_levellocals.h"
|
2017-01-01 09:28:35 +00:00
|
|
|
#include "r_wallsetup.h"
|
|
|
|
#include "v_palette.h"
|
2017-01-12 15:21:46 +00:00
|
|
|
#include "r_utility.h"
|
2017-01-01 09:28:35 +00:00
|
|
|
#include "r_data/colormaps.h"
|
|
|
|
#include "swrenderer/r_memory.h"
|
2017-01-11 19:42:39 +00:00
|
|
|
#include "swrenderer/scene/r_opaque_pass.h"
|
2017-01-03 06:13:40 +00:00
|
|
|
#include "swrenderer/scene/r_3dfloors.h"
|
|
|
|
#include "swrenderer/scene/r_portal.h"
|
2017-01-12 15:21:46 +00:00
|
|
|
#include "swrenderer/scene/r_light.h"
|
|
|
|
#include "swrenderer/scene/r_scene.h"
|
2017-02-02 14:24:21 +00:00
|
|
|
#include "swrenderer/viewport/r_viewport.h"
|
2017-01-01 09:28:35 +00:00
|
|
|
#include "swrenderer/line/r_line.h"
|
2017-01-03 06:13:40 +00:00
|
|
|
#include "swrenderer/line/r_walldraw.h"
|
|
|
|
#include "swrenderer/line/r_wallsetup.h"
|
|
|
|
#include "swrenderer/drawers/r_draw.h"
|
|
|
|
#include "swrenderer/segments/r_clipsegment.h"
|
|
|
|
#include "swrenderer/segments/r_drawsegment.h"
|
|
|
|
#include "swrenderer/plane/r_visibleplane.h"
|
2017-01-11 22:27:35 +00:00
|
|
|
#include "swrenderer/plane/r_visibleplanelist.h"
|
2017-01-03 06:13:40 +00:00
|
|
|
#include "swrenderer/things/r_decal.h"
|
2017-02-03 23:25:37 +00:00
|
|
|
#include "swrenderer/r_renderthread.h"
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
CVAR(Bool, r_fogboundary, true, 0)
|
|
|
|
CVAR(Bool, r_drawmirrors, true, 0)
|
2017-01-28 22:37:57 +00:00
|
|
|
EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor);
|
2017-01-01 09:28:35 +00:00
|
|
|
|
|
|
|
namespace swrenderer
|
|
|
|
{
|
2017-02-03 23:25:37 +00:00
|
|
|
SWRenderLine::SWRenderLine(RenderThread *thread)
|
|
|
|
{
|
|
|
|
Thread = thread;
|
|
|
|
}
|
|
|
|
|
2017-01-19 02:11:49 +00:00
|
|
|
void SWRenderLine::Render(seg_t *line, subsector_t *subsector, sector_t *sector, sector_t *fakebacksector, VisiblePlane *linefloorplane, VisiblePlane *lineceilingplane, bool infog, FDynamicColormap *colormap)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
bool solid;
|
|
|
|
DVector2 pt1, pt2;
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
mSubsector = subsector;
|
|
|
|
mFrontSector = sector;
|
|
|
|
mBackSector = fakebacksector;
|
|
|
|
mFloorPlane = linefloorplane;
|
|
|
|
mCeilingPlane = lineceilingplane;
|
2017-01-12 19:13:21 +00:00
|
|
|
foggy = infog;
|
2017-01-12 20:29:19 +00:00
|
|
|
basecolormap = colormap;
|
2017-02-11 17:00:02 +00:00
|
|
|
mLineSegment = line;
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
pt1 = line->v1->fPos() - ViewPos;
|
|
|
|
pt2 = line->v2->fPos() - ViewPos;
|
|
|
|
|
|
|
|
// Reject lines not facing viewer
|
|
|
|
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
|
|
|
|
return;
|
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
if (WallC.Init(Thread, pt1, pt2, 32.0 / (1 << 12)))
|
2017-01-03 06:13:40 +00:00
|
|
|
return;
|
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderPortal *renderportal = Thread->Portal.get();
|
2017-01-05 03:55:26 +00:00
|
|
|
|
|
|
|
if (WallC.sx1 >= renderportal->WindowRight || WallC.sx2 <= renderportal->WindowLeft)
|
2017-01-03 06:13:40 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (line->linedef == NULL)
|
|
|
|
{
|
2017-02-03 23:25:37 +00:00
|
|
|
if (Thread->ClipSegments->Check(WallC.sx1, WallC.sx2))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
mSubsector->flags |= SSECF_DRAWN;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reject lines that aren't seen from the portal (if any)
|
|
|
|
// [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes.
|
2017-01-05 03:55:26 +00:00
|
|
|
if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && P_ClipLineToPortal(line->linedef, renderportal->CurrentPortal->dst, ViewPos))
|
2017-01-03 06:13:40 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
vertex_t *v1, *v2;
|
|
|
|
v1 = line->linedef->v1;
|
|
|
|
v2 = line->linedef->v2;
|
|
|
|
|
|
|
|
if ((v1 == line->v1 && v2 == line->v2) || (v2 == line->v1 && v1 == line->v2))
|
|
|
|
{ // The seg is the entire wall.
|
2017-02-03 23:25:37 +00:00
|
|
|
WallT.InitFromWallCoords(Thread, &WallC);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // The seg is only part of the wall.
|
|
|
|
if (line->linedef->sidedef[0] != line->sidedef)
|
|
|
|
{
|
|
|
|
swapvalues(v1, v2);
|
|
|
|
}
|
2017-02-03 23:25:37 +00:00
|
|
|
WallT.InitFromLine(Thread, v1->fPos() - ViewPos, v2->fPos() - ViewPos);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-06 15:04:27 +00:00
|
|
|
Clip3DFloors *clip3d = Thread->Clip3D.get();
|
2017-01-04 17:54:14 +00:00
|
|
|
|
|
|
|
if (!(clip3d->fake3D & FAKE3D_FAKEBACK))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
mBackSector = line->backsector;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
mFrontCeilingZ1 = mFrontSector->ceilingplane.ZatPoint(line->v1);
|
|
|
|
mFrontFloorZ1 = mFrontSector->floorplane.ZatPoint(line->v1);
|
|
|
|
mFrontCeilingZ2 = mFrontSector->ceilingplane.ZatPoint(line->v2);
|
|
|
|
mFrontFloorZ2 = mFrontSector->floorplane.ZatPoint(line->v2);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
rw_havehigh = rw_havelow = false;
|
|
|
|
|
|
|
|
// Single sided line?
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mBackSector == NULL)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
solid = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// kg3D - its fake, no transfer_heights
|
2017-01-04 17:54:14 +00:00
|
|
|
if (!(clip3d->fake3D & FAKE3D_FAKEBACK))
|
2017-01-03 06:13:40 +00:00
|
|
|
{ // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water
|
2017-02-11 17:00:02 +00:00
|
|
|
mBackSector = Thread->OpaquePass->FakeFlat(mBackSector, &tempsec, nullptr, nullptr, mLineSegment, WallC.sx1, WallC.sx2, mFrontCeilingZ1, mFrontCeilingZ2);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
mDoorClosed = false; // killough 4/16/98
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
mBackCeilingZ1 = mBackSector->ceilingplane.ZatPoint(line->v1);
|
|
|
|
mBackFloorZ1 = mBackSector->floorplane.ZatPoint(line->v1);
|
|
|
|
mBackCeilingZ2 = mBackSector->ceilingplane.ZatPoint(line->v2);
|
|
|
|
mBackFloorZ2 = mBackSector->floorplane.ZatPoint(line->v2);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-01-04 17:54:14 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_FAKEBACK)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontFloorZ1 >= mBackFloorZ1 && mFrontFloorZ2 >= mBackFloorZ2)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-04 17:54:14 +00:00
|
|
|
clip3d->fake3D |= FAKE3D_CLIPBOTFRONT;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontCeilingZ1 <= mBackCeilingZ1 && mFrontCeilingZ2 <= mBackCeilingZ2)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-04 17:54:14 +00:00
|
|
|
clip3d->fake3D |= FAKE3D_CLIPTOPFRONT;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cannot make these walls solid, because it can result in
|
|
|
|
// sprite clipping problems for sprites near the wall
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontCeilingZ1 > mBackCeilingZ1 || mFrontCeilingZ2 > mBackCeilingZ2)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
rw_havehigh = true;
|
2017-02-11 17:00:02 +00:00
|
|
|
wallupper.Project(mBackSector->ceilingplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontFloorZ1 < mBackFloorZ1 || mFrontFloorZ2 < mBackFloorZ2)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
rw_havelow = true;
|
2017-02-11 17:00:02 +00:00
|
|
|
walllower.Project(mBackSector->floorplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Portal
|
|
|
|
if (line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0])
|
|
|
|
{
|
|
|
|
solid = true;
|
|
|
|
}
|
|
|
|
// Closed door.
|
2017-02-11 17:00:02 +00:00
|
|
|
else if ((mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) ||
|
|
|
|
(mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
solid = true;
|
|
|
|
}
|
|
|
|
else if (
|
|
|
|
// properly render skies (consider door "open" if both ceilings are sky):
|
2017-02-11 17:00:02 +00:00
|
|
|
(mBackSector->GetTexture(sector_t::ceiling) != skyflatnum || mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum)
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// if door is closed because back is shut:
|
2017-02-11 17:00:02 +00:00
|
|
|
&& mBackCeilingZ1 <= mBackFloorZ1 && mBackCeilingZ2 <= mBackFloorZ2
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// preserve a kind of transparent door/lift special effect:
|
2017-02-11 17:00:02 +00:00
|
|
|
&& ((mBackCeilingZ1 >= mFrontCeilingZ1 && mBackCeilingZ2 >= mFrontCeilingZ2) || line->sidedef->GetTexture(side_t::top).isValid())
|
|
|
|
&& ((mBackFloorZ1 <= mFrontFloorZ1 && mBackFloorZ2 <= mFrontFloorZ2) || line->sidedef->GetTexture(side_t::bottom).isValid()))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
// killough 1/18/98 -- This function is used to fix the automap bug which
|
|
|
|
// showed lines behind closed doors simply because the door had a dropoff.
|
|
|
|
//
|
|
|
|
// It assumes that Doom has already ruled out a door being closed because
|
|
|
|
// of front-back closure (e.g. front floor is taller than back ceiling).
|
|
|
|
|
|
|
|
// This fixes the automap floor height bug -- killough 1/18/98:
|
|
|
|
// killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c
|
2017-02-11 17:00:02 +00:00
|
|
|
mDoorClosed = true;
|
2017-01-03 06:13:40 +00:00
|
|
|
solid = true;
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
else if (mFrontSector->ceilingplane != mBackSector->ceilingplane ||
|
|
|
|
mFrontSector->floorplane != mBackSector->floorplane)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
// Window.
|
|
|
|
solid = false;
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
else if (SkyboxCompare(mFrontSector, mBackSector))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
solid = false;
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
else if (mBackSector->lightlevel != mFrontSector->lightlevel
|
|
|
|
|| mBackSector->GetTexture(sector_t::floor) != mFrontSector->GetTexture(sector_t::floor)
|
|
|
|
|| mBackSector->GetTexture(sector_t::ceiling) != mFrontSector->GetTexture(sector_t::ceiling)
|
|
|
|
|| mLineSegment->sidedef->GetTexture(side_t::mid).isValid()
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// killough 3/7/98: Take flats offsets into account:
|
2017-02-11 17:00:02 +00:00
|
|
|
|| mBackSector->planes[sector_t::floor].xform != mFrontSector->planes[sector_t::floor].xform
|
|
|
|
|| mBackSector->planes[sector_t::ceiling].xform != mFrontSector->planes[sector_t::ceiling].xform
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
|| mBackSector->GetPlaneLight(sector_t::floor) != mFrontSector->GetPlaneLight(sector_t::floor)
|
|
|
|
|| mBackSector->GetPlaneLight(sector_t::ceiling) != mFrontSector->GetPlaneLight(sector_t::ceiling)
|
|
|
|
|| mBackSector->GetVisFlags(sector_t::floor) != mFrontSector->GetVisFlags(sector_t::floor)
|
|
|
|
|| mBackSector->GetVisFlags(sector_t::ceiling) != mFrontSector->GetVisFlags(sector_t::ceiling)
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// [RH] Also consider colormaps
|
2017-02-11 17:00:02 +00:00
|
|
|
|| mBackSector->ColorMap != mFrontSector->ColorMap
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// kg3D - and fake lights
|
2017-02-11 17:00:02 +00:00
|
|
|
|| (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size())
|
|
|
|
|| (mBackSector->e && mBackSector->e->XFloor.lightlist.Size())
|
2017-01-03 06:13:40 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
solid = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Reject empty lines used for triggers and special events.
|
|
|
|
// Identical floor and ceiling on both sides, identical light levels
|
|
|
|
// on both sides, and no middle texture.
|
|
|
|
|
|
|
|
// When using GL nodes, do a clipping test for these lines so we can
|
|
|
|
// mark their subsectors as visible for automap texturing.
|
2017-02-11 17:00:02 +00:00
|
|
|
if (hasglnodes && !(mSubsector->flags & SSECF_DRAWN))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-03 23:25:37 +00:00
|
|
|
if (Thread->ClipSegments->Check(WallC.sx1, WallC.sx2))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
mSubsector->flags |= SSECF_DRAWN;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rw_prepped = false;
|
|
|
|
|
|
|
|
if (line->linedef->special == Line_Horizon)
|
|
|
|
{
|
|
|
|
// Be aware: Line_Horizon does not work properly with sloped planes
|
2017-01-24 05:50:17 +00:00
|
|
|
fillshort(walltop.ScreenY + WallC.sx1, WallC.sx2 - WallC.sx1, centery);
|
|
|
|
fillshort(wallbottom.ScreenY + WallC.sx1, WallC.sx2 - WallC.sx1, centery);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
mCeilingClipped = walltop.Project(mFrontSector->ceilingplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP);
|
|
|
|
mFloorClipped = wallbottom.Project(mFrontSector->floorplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 14:51:54 +00:00
|
|
|
bool visible = Thread->ClipSegments->Clip(WallC.sx1, WallC.sx2, solid, this);
|
2017-01-03 17:57:48 +00:00
|
|
|
|
|
|
|
if (visible)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
mSubsector->flags |= SSECF_DRAWN;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 18:08:02 +00:00
|
|
|
bool SWRenderLine::SkyboxCompare(sector_t *frontsector, sector_t *backsector) const
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
FSectorPortal *frontc = frontsector->GetPortal(sector_t::ceiling);
|
|
|
|
FSectorPortal *frontf = frontsector->GetPortal(sector_t::floor);
|
|
|
|
FSectorPortal *backc = backsector->GetPortal(sector_t::ceiling);
|
|
|
|
FSectorPortal *backf = backsector->GetPortal(sector_t::floor);
|
|
|
|
|
|
|
|
// return true if any of the planes has a linedef-based portal (unless both sides have the same one.
|
|
|
|
// Ideally this should also check thing based portals but the omission of this check had been abused to hell and back for those.
|
|
|
|
// (Note: This may require a compatibility option if some maps ran into this for line based portals as well.)
|
|
|
|
if (!frontc->MergeAllowed()) return (frontc != backc);
|
|
|
|
if (!frontf->MergeAllowed()) return (frontf != backf);
|
|
|
|
if (!backc->MergeAllowed()) return true;
|
|
|
|
if (!backf->MergeAllowed()) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A wall segment will be drawn between start and stop pixels (inclusive).
|
2017-01-03 18:08:02 +00:00
|
|
|
bool SWRenderLine::RenderWallSegment(int start, int stop)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
bool maskedtexture = false;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
|
|
|
if (start >= viewwidth || start >= stop)
|
|
|
|
I_FatalError("Bad R_StoreWallRange: %i to %i", start, stop);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!rw_prepped)
|
|
|
|
{
|
|
|
|
rw_prepped = true;
|
2017-01-03 18:08:02 +00:00
|
|
|
SetWallVariables(true);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
side_t *sidedef = mLineSegment->sidedef;
|
|
|
|
|
2017-01-03 06:13:40 +00:00
|
|
|
rw_offset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid));
|
|
|
|
rw_light = rw_lightleft + rw_lightstep * (start - WallC.sx1);
|
2017-01-05 03:55:26 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderPortal *renderportal = Thread->Portal.get();
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-04 01:50:52 +00:00
|
|
|
DrawSegment *draw_segment = Thread->FrameMemory->NewObject<DrawSegment>();
|
2017-02-03 23:25:37 +00:00
|
|
|
Thread->DrawSegments->Push(draw_segment);
|
2017-02-03 20:11:55 +00:00
|
|
|
|
2017-01-05 03:55:26 +00:00
|
|
|
draw_segment->CurrentPortalUniq = renderportal->CurrentPortalUniq;
|
2017-01-03 06:13:40 +00:00
|
|
|
draw_segment->sx1 = WallC.sx1;
|
|
|
|
draw_segment->sx2 = WallC.sx2;
|
|
|
|
draw_segment->sz1 = WallC.sz1;
|
|
|
|
draw_segment->sz2 = WallC.sz2;
|
|
|
|
draw_segment->cx = WallC.tleft.X;;
|
|
|
|
draw_segment->cy = WallC.tleft.Y;
|
|
|
|
draw_segment->cdx = WallC.tright.X - WallC.tleft.X;
|
|
|
|
draw_segment->cdy = WallC.tright.Y - WallC.tleft.Y;
|
|
|
|
draw_segment->tmapvals = WallT;
|
|
|
|
draw_segment->siz1 = 1 / WallC.sz1;
|
|
|
|
draw_segment->siz2 = 1 / WallC.sz2;
|
|
|
|
draw_segment->x1 = start;
|
|
|
|
draw_segment->x2 = stop;
|
2017-02-11 17:00:02 +00:00
|
|
|
draw_segment->curline = mLineSegment;
|
2017-01-03 06:13:40 +00:00
|
|
|
draw_segment->bFogBoundary = false;
|
|
|
|
draw_segment->bFakeBoundary = false;
|
2017-01-12 19:13:21 +00:00
|
|
|
draw_segment->foggy = foggy;
|
2017-01-04 17:54:14 +00:00
|
|
|
|
2017-02-06 15:04:27 +00:00
|
|
|
Clip3DFloors *clip3d = Thread->Clip3D.get();
|
2017-01-04 17:54:14 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_FAKEMASK) draw_segment->fake = 1;
|
2017-01-03 06:13:40 +00:00
|
|
|
else draw_segment->fake = 0;
|
|
|
|
|
2017-01-15 21:21:21 +00:00
|
|
|
draw_segment->sprtopclip = nullptr;
|
|
|
|
draw_segment->sprbottomclip = nullptr;
|
|
|
|
draw_segment->maskedtexturecol = nullptr;
|
|
|
|
draw_segment->bkup = nullptr;
|
|
|
|
draw_segment->swall = nullptr;
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:52:49 +00:00
|
|
|
bool markportal = ShouldMarkPortal();
|
|
|
|
|
|
|
|
if (markportal)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
draw_segment->silhouette = SIL_BOTH;
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
else if (mBackSector == NULL)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
|
|
|
draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
2017-01-15 21:21:21 +00:00
|
|
|
fillshort(draw_segment->sprtopclip, stop - start, viewheight);
|
|
|
|
memset(draw_segment->sprbottomclip, -1, (stop - start) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
draw_segment->silhouette = SIL_BOTH;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// two sided line
|
|
|
|
draw_segment->silhouette = 0;
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontFloorZ1 > mBackFloorZ1 || mFrontFloorZ2 > mBackFloorZ2 ||
|
|
|
|
mBackSector->floorplane.PointOnSide(ViewPos) < 0)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
draw_segment->silhouette = SIL_BOTTOM;
|
|
|
|
}
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontCeilingZ1 < mBackCeilingZ1 || mFrontCeilingZ2 < mBackCeilingZ2 ||
|
|
|
|
mBackSector->ceilingplane.PointOnSide(ViewPos) < 0)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
draw_segment->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
|
|
|
|
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mDoorClosed || (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
2017-01-15 21:21:21 +00:00
|
|
|
memset(draw_segment->sprbottomclip, -1, (stop - start) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
draw_segment->silhouette |= SIL_BOTTOM;
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mDoorClosed || (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2))
|
2017-01-03 06:13:40 +00:00
|
|
|
{ // killough 1/17/98, 2/8/98
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
2017-01-15 21:21:21 +00:00
|
|
|
fillshort(draw_segment->sprtopclip, stop - start, viewheight);
|
2017-01-03 06:13:40 +00:00
|
|
|
draw_segment->silhouette |= SIL_TOP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
if (!draw_segment->fake && r_3dfloors && mBackSector->e && mBackSector->e->XFloor.ffloors.Size()) {
|
|
|
|
for (i = 0; i < (int)mBackSector->e->XFloor.ffloors.Size(); i++) {
|
|
|
|
F3DFloor *rover = mBackSector->e->XFloor.ffloors[i];
|
2017-01-03 06:13:40 +00:00
|
|
|
if (rover->flags & FF_RENDERSIDES && (!(rover->flags & FF_INVERTSIDES) || rover->flags & FF_ALLSIDES)) {
|
|
|
|
draw_segment->bFakeBoundary |= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
if (!draw_segment->fake && r_3dfloors && mFrontSector->e && mFrontSector->e->XFloor.ffloors.Size()) {
|
|
|
|
for (i = 0; i < (int)mFrontSector->e->XFloor.ffloors.Size(); i++) {
|
|
|
|
F3DFloor *rover = mFrontSector->e->XFloor.ffloors[i];
|
2017-01-03 06:13:40 +00:00
|
|
|
if (rover->flags & FF_RENDERSIDES && (rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) {
|
|
|
|
draw_segment->bFakeBoundary |= 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// kg3D - no for fakes
|
|
|
|
if (!draw_segment->fake)
|
|
|
|
// allocate space for masked texture tables, if needed
|
|
|
|
// [RH] Don't just allocate the space; fill it in too.
|
2017-02-11 17:00:02 +00:00
|
|
|
if ((TexMan(sidedef->GetTexture(side_t::mid), true)->UseType != FTexture::TEX_Null || draw_segment->bFakeBoundary || IsFogBoundary(mFrontSector, mBackSector)) &&
|
|
|
|
(mCeilingClipped != ProjectedWallCull::OutsideBelow || !sidedef->GetTexture(side_t::top).isValid()) &&
|
|
|
|
(mFloorClipped != ProjectedWallCull::OutsideAbove || !sidedef->GetTexture(side_t::bottom).isValid()) &&
|
2017-01-03 06:13:40 +00:00
|
|
|
(WallC.sz1 >= TOO_CLOSE_Z && WallC.sz2 >= TOO_CLOSE_Z))
|
|
|
|
{
|
|
|
|
float *swal;
|
|
|
|
fixed_t *lwal;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
maskedtexture = true;
|
|
|
|
|
|
|
|
// kg3D - backup for mid and fake walls
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->bkup = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
2017-02-03 23:25:37 +00:00
|
|
|
memcpy(draw_segment->bkup, &Thread->OpaquePass->ceilingclip[start], sizeof(short)*(stop - start));
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
draw_segment->bFogBoundary = IsFogBoundary(mFrontSector, mBackSector);
|
2017-01-03 06:13:40 +00:00
|
|
|
if (sidedef->GetTexture(side_t::mid).isValid() || draw_segment->bFakeBoundary)
|
|
|
|
{
|
|
|
|
if (sidedef->GetTexture(side_t::mid).isValid())
|
|
|
|
draw_segment->bFakeBoundary |= 4; // it is also mid texture
|
|
|
|
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->maskedtexturecol = Thread->FrameMemory->AllocMemory<fixed_t>(stop - start);
|
|
|
|
draw_segment->swall = Thread->FrameMemory->AllocMemory<float>(stop - start);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-01-15 21:21:21 +00:00
|
|
|
lwal = draw_segment->maskedtexturecol;
|
|
|
|
swal = draw_segment->swall;
|
2017-01-03 06:13:40 +00:00
|
|
|
FTexture *pic = TexMan(sidedef->GetTexture(side_t::mid), true);
|
|
|
|
double yscale = pic->Scale.Y * sidedef->GetTextureYScale(side_t::mid);
|
|
|
|
fixed_t xoffset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid));
|
|
|
|
|
|
|
|
if (pic->bWorldPanning)
|
|
|
|
{
|
|
|
|
xoffset = xs_RoundToInt(xoffset * lwallscale);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = start; i < stop; i++)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
*lwal++ = walltexcoords.UPos[i] + xoffset;
|
|
|
|
*swal++ = walltexcoords.VStep[i];
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-01-15 21:21:21 +00:00
|
|
|
double istart = draw_segment->swall[0] * yscale;
|
2017-01-03 06:13:40 +00:00
|
|
|
double iend = *(swal - 1) * yscale;
|
|
|
|
#if 0
|
|
|
|
///This was for avoiding overflow when using fixed point. It might not be needed anymore.
|
|
|
|
const double mini = 3 / 65536.0;
|
|
|
|
if (istart < mini && istart >= 0) istart = mini;
|
|
|
|
if (istart > -mini && istart < 0) istart = -mini;
|
|
|
|
if (iend < mini && iend >= 0) iend = mini;
|
|
|
|
if (iend > -mini && iend < 0) iend = -mini;
|
|
|
|
#endif
|
|
|
|
istart = 1 / istart;
|
|
|
|
iend = 1 / iend;
|
|
|
|
draw_segment->yscale = (float)yscale;
|
|
|
|
draw_segment->iscale = (float)istart;
|
|
|
|
if (stop - start > 0)
|
|
|
|
{
|
|
|
|
draw_segment->iscalestep = float((iend - istart) / (stop - start));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
draw_segment->iscalestep = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
draw_segment->light = rw_light;
|
|
|
|
draw_segment->lightstep = rw_lightstep;
|
|
|
|
|
|
|
|
// Masked midtextures should get the light level from the sector they reference,
|
|
|
|
// not from the current subsector, which is what the current wallshade value
|
|
|
|
// comes from. We make an exeption for polyobjects, however, since their "home"
|
|
|
|
// sector should be whichever one they move into.
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mLineSegment->sidedef->Flags & WALLF_POLYOBJ)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
draw_segment->shade = wallshade;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
draw_segment->shade = LIGHT2SHADE(mLineSegment->sidedef->GetLightLevel(foggy, mLineSegment->frontsector->lightlevel) + R_ActualExtraLight(foggy));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-01-15 21:21:21 +00:00
|
|
|
if (draw_segment->bFogBoundary || draw_segment->maskedtexturecol != nullptr)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-03 23:25:37 +00:00
|
|
|
Thread->DrawSegments->PushInteresting(draw_segment);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// render it
|
|
|
|
if (markceiling)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mCeilingPlane)
|
2017-01-03 06:13:40 +00:00
|
|
|
{ // killough 4/11/98: add NULL ptr checks
|
2017-02-11 17:00:02 +00:00
|
|
|
mCeilingPlane = Thread->PlaneList->GetRange(mCeilingPlane, start, stop);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
markceiling = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (markfloor)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFloorPlane)
|
2017-01-03 06:13:40 +00:00
|
|
|
{ // killough 4/11/98: add NULL ptr checks
|
2017-02-11 17:00:02 +00:00
|
|
|
mFloorPlane = Thread->PlaneList->GetRange(mFloorPlane, start, stop);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
markfloor = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 18:08:02 +00:00
|
|
|
RenderWallSegmentTextures(start, stop);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-01-11 22:08:24 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_FAKEMASK)
|
|
|
|
{
|
2017-01-04 17:54:14 +00:00
|
|
|
return (clip3d->fake3D & FAKE3D_FAKEMASK) == 0;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// save sprite clipping info
|
2017-01-15 21:21:21 +00:00
|
|
|
if (((draw_segment->silhouette & SIL_TOP) || maskedtexture) && draw_segment->sprtopclip == nullptr)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
2017-02-03 23:25:37 +00:00
|
|
|
memcpy(draw_segment->sprtopclip, &Thread->OpaquePass->ceilingclip[start], sizeof(short)*(stop - start));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-01-15 21:21:21 +00:00
|
|
|
if (((draw_segment->silhouette & SIL_BOTTOM) || maskedtexture) && draw_segment->sprbottomclip == nullptr)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-04 01:50:52 +00:00
|
|
|
draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory<short>(stop - start);
|
2017-02-03 23:25:37 +00:00
|
|
|
memcpy(draw_segment->sprbottomclip, &Thread->OpaquePass->floorclip[start], sizeof(short)*(stop - start));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
if (maskedtexture && mLineSegment->sidedef->GetTexture(side_t::mid).isValid())
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
draw_segment->silhouette |= SIL_TOP | SIL_BOTTOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Draw any decals bound to the seg
|
|
|
|
// [ZZ] Only if not an active mirror
|
2017-02-11 17:52:49 +00:00
|
|
|
if (!markportal)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
RenderDecal::RenderDecals(Thread, mLineSegment->sidedef, draw_segment, wallshade, rw_lightleft, rw_lightstep, mLineSegment, WallC, foggy, basecolormap, walltop.ScreenY, wallbottom.ScreenY);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 17:52:49 +00:00
|
|
|
if (markportal)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
Thread->Portal->AddLinePortal(mLineSegment->linedef, draw_segment->x1, draw_segment->x2, draw_segment->sprtopclip, draw_segment->sprbottomclip);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-01-04 17:54:14 +00:00
|
|
|
return (clip3d->fake3D & FAKE3D_FAKEMASK) == 0;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 17:52:49 +00:00
|
|
|
bool SWRenderLine::ShouldMarkFloor() const
|
|
|
|
{
|
|
|
|
// deep water check
|
|
|
|
if (mFrontSector->GetHeightSec() == nullptr)
|
|
|
|
{
|
|
|
|
int planeside = mFrontSector->floorplane.PointOnSide(ViewPos);
|
|
|
|
if (mFrontSector->floorplane.fC() < 0) // 3D floors have the floor backwards
|
|
|
|
planeside = -planeside;
|
|
|
|
if (planeside <= 0) // above view plane
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
side_t *sidedef = mLineSegment->sidedef;
|
|
|
|
line_t *linedef = mLineSegment->linedef;
|
|
|
|
|
|
|
|
if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (mBackSector == nullptr) // single sided line
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else // two-sided line
|
|
|
|
{
|
|
|
|
if (linedef->isVisualPortal()) return true;
|
|
|
|
|
|
|
|
// closed door
|
|
|
|
if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true;
|
|
|
|
if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true;
|
|
|
|
|
|
|
|
if (mBackSector->floorplane != mFrontSector->floorplane) return true;
|
|
|
|
if (mBackSector->lightlevel != mFrontSector->lightlevel) return true;
|
|
|
|
if (mBackSector->GetTexture(sector_t::floor) != mFrontSector->GetTexture(sector_t::floor)) return true;
|
|
|
|
if (mBackSector->GetPlaneLight(sector_t::floor) != mFrontSector->GetPlaneLight(sector_t::floor)) return true;
|
|
|
|
|
|
|
|
// Add checks for (x,y) offsets
|
|
|
|
if (mBackSector->planes[sector_t::floor].xform != mFrontSector->planes[sector_t::floor].xform) return true;
|
|
|
|
if (mBackSector->GetAlpha(sector_t::floor) != mFrontSector->GetAlpha(sector_t::floor)) return true;
|
|
|
|
|
|
|
|
// prevent 2s normals from bleeding through deep water
|
|
|
|
if (mFrontSector->heightsec) return true;
|
|
|
|
|
|
|
|
if (mBackSector->GetVisFlags(sector_t::floor) != mFrontSector->GetVisFlags(sector_t::floor)) return true;
|
|
|
|
if (mBackSector->ColorMap != mFrontSector->ColorMap) return true;
|
|
|
|
if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return true;
|
|
|
|
if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return true;
|
|
|
|
|
|
|
|
if (sidedef->GetTexture(side_t::mid).isValid() && ((linedef->flags & (ML_CLIP_MIDTEX | ML_WRAP_MIDTEX)) || sidedef->Flags & (WALLF_CLIP_MIDTEX | WALLF_WRAP_MIDTEX))) return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SWRenderLine::ShouldMarkCeiling() const
|
|
|
|
{
|
|
|
|
// deep water check
|
|
|
|
if (mFrontSector->GetHeightSec() == nullptr && mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum)
|
|
|
|
{
|
|
|
|
int planeside = mFrontSector->ceilingplane.PointOnSide(ViewPos);
|
|
|
|
if (mFrontSector->ceilingplane.fC() > 0) // 3D floors have the ceiling backwards
|
|
|
|
planeside = -planeside;
|
|
|
|
if (planeside <= 0) // below view plane
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
side_t *sidedef = mLineSegment->sidedef;
|
|
|
|
line_t *linedef = mLineSegment->linedef;
|
|
|
|
|
|
|
|
if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (mBackSector == nullptr) // single sided line
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else // two-sided line
|
|
|
|
{
|
|
|
|
if (linedef->isVisualPortal()) return true;
|
|
|
|
|
|
|
|
// closed door
|
|
|
|
if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true;
|
|
|
|
if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true;
|
|
|
|
|
|
|
|
if (mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum || mBackSector->GetTexture(sector_t::ceiling) != skyflatnum)
|
|
|
|
{
|
|
|
|
if (mBackSector->ceilingplane != mFrontSector->ceilingplane) return true;
|
|
|
|
if (mBackSector->lightlevel != mFrontSector->lightlevel) return true;
|
|
|
|
if (mBackSector->GetTexture(sector_t::ceiling) != mFrontSector->GetTexture(sector_t::ceiling)) return true;
|
|
|
|
|
|
|
|
// Add checks for (x,y) offsets
|
|
|
|
if (mBackSector->planes[sector_t::ceiling].xform != mFrontSector->planes[sector_t::ceiling].xform) return true;
|
|
|
|
if (mBackSector->GetAlpha(sector_t::ceiling) != mFrontSector->GetAlpha(sector_t::ceiling)) return true;
|
|
|
|
|
|
|
|
// prevent 2s normals from bleeding through fake ceilings
|
|
|
|
if (mFrontSector->heightsec && mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum) return true;
|
|
|
|
|
|
|
|
if (mBackSector->GetPlaneLight(sector_t::ceiling) != mFrontSector->GetPlaneLight(sector_t::ceiling)) return true;
|
|
|
|
if (mBackSector->GetFlags(sector_t::ceiling) != mFrontSector->GetFlags(sector_t::ceiling)) return true;
|
|
|
|
|
|
|
|
if (mBackSector->ColorMap != mFrontSector->ColorMap) return true;
|
|
|
|
if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return true;
|
|
|
|
if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return true;
|
|
|
|
|
|
|
|
if (sidedef->GetTexture(side_t::mid).isValid())
|
|
|
|
{
|
|
|
|
if (linedef->flags & (ML_CLIP_MIDTEX | ML_WRAP_MIDTEX)) return true;
|
|
|
|
if (sidedef->Flags & (WALLF_CLIP_MIDTEX | WALLF_WRAP_MIDTEX)) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SWRenderLine::ShouldMarkPortal() const
|
|
|
|
{
|
|
|
|
side_t *sidedef = mLineSegment->sidedef;
|
|
|
|
line_t *linedef = mLineSegment->linedef;
|
|
|
|
|
|
|
|
if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return linedef->isVisualPortal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 18:08:02 +00:00
|
|
|
void SWRenderLine::SetWallVariables(bool needlights)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
double rowoffset;
|
|
|
|
double yrepeat;
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
side_t *sidedef = mLineSegment->sidedef;
|
|
|
|
line_t *linedef = mLineSegment->linedef;
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// mark the segment as visible for auto map
|
2017-02-03 23:25:37 +00:00
|
|
|
if (!Thread->Scene->DontMapLines()) linedef->flags |= ML_MAPPED;
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:52:49 +00:00
|
|
|
markfloor = ShouldMarkFloor();
|
|
|
|
markceiling = ShouldMarkCeiling();
|
|
|
|
|
2017-01-03 06:13:40 +00:00
|
|
|
midtexture = toptexture = bottomtexture = 0;
|
|
|
|
|
|
|
|
if (sidedef == linedef->sidedef[0] &&
|
|
|
|
(linedef->special == Line_Mirror && r_drawmirrors)) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals
|
|
|
|
{
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
else if (mBackSector == NULL)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
// single sided line
|
2017-02-11 17:52:49 +00:00
|
|
|
|
2017-01-03 06:13:40 +00:00
|
|
|
// [RH] Horizon lines do not need to be textured
|
|
|
|
if (linedef->isVisualPortal())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else if (linedef->special != Line_Horizon)
|
|
|
|
{
|
|
|
|
midtexture = TexMan(sidedef->GetTexture(side_t::mid), true);
|
|
|
|
rw_offset_mid = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid));
|
|
|
|
rowoffset = sidedef->GetTextureYOffset(side_t::mid);
|
|
|
|
rw_midtexturescalex = sidedef->GetTextureXScale(side_t::mid);
|
|
|
|
rw_midtexturescaley = sidedef->GetTextureYScale(side_t::mid);
|
|
|
|
yrepeat = midtexture->Scale.Y * rw_midtexturescaley;
|
|
|
|
if (yrepeat >= 0)
|
|
|
|
{ // normal orientation
|
|
|
|
if (linedef->flags & ML_DONTPEGBOTTOM)
|
|
|
|
{ // bottom of texture at bottom
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_midtexturemid = (mFrontSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + midtexture->GetHeight();
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // top of texture at top
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_midtexturemid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat;
|
2017-01-03 06:13:40 +00:00
|
|
|
if (rowoffset < 0 && midtexture != NULL)
|
|
|
|
{
|
|
|
|
rowoffset += midtexture->GetHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // upside down
|
|
|
|
rowoffset = -rowoffset;
|
|
|
|
if (linedef->flags & ML_DONTPEGBOTTOM)
|
|
|
|
{ // top of texture at bottom
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_midtexturemid = (mFrontSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // bottom of texture at top
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_midtexturemid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + midtexture->GetHeight();
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (midtexture->bWorldPanning)
|
|
|
|
{
|
|
|
|
rw_midtexturemid += rowoffset * yrepeat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// rowoffset is added outside the multiply so that it positions the texture
|
|
|
|
// by texels instead of world units.
|
|
|
|
rw_midtexturemid += rowoffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // two-sided line
|
|
|
|
// hack to allow height changes in outdoor areas
|
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
double rw_frontlowertop = mFrontSector->GetPlaneTexZ(sector_t::ceiling);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
|
|
|
mBackSector->GetTexture(sector_t::ceiling) == skyflatnum)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
if (rw_havehigh)
|
|
|
|
{ // front ceiling is above back ceiling
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(&walltop.ScreenY[WallC.sx1], &wallupper.ScreenY[WallC.sx1], (WallC.sx2 - WallC.sx1) * sizeof(walltop.ScreenY[0]));
|
2017-01-03 06:13:40 +00:00
|
|
|
rw_havehigh = false;
|
|
|
|
}
|
2017-02-11 17:00:02 +00:00
|
|
|
else if (rw_havelow && mFrontSector->ceilingplane != mBackSector->ceilingplane)
|
2017-01-03 06:13:40 +00:00
|
|
|
{ // back ceiling is above front ceiling
|
|
|
|
// The check for rw_havelow is not Doom-compliant, but it avoids HoM that
|
|
|
|
// would otherwise occur because there is space made available for this
|
|
|
|
// wall but nothing to draw for it.
|
|
|
|
// Recalculate walltop so that the wall is clipped by the back sector's
|
|
|
|
// ceiling instead of the front sector's ceiling.
|
2017-02-11 17:00:02 +00:00
|
|
|
walltop.Project(mBackSector->ceilingplane, &WallC, mLineSegment, Thread->Portal->MirrorFlags & RF_XFLIP);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
// Putting sky ceilings on the front and back of a line alters the way unpegged
|
|
|
|
// positioning works.
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_frontlowertop = mBackSector->GetPlaneTexZ(sector_t::ceiling);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rw_havehigh)
|
|
|
|
{ // top texture
|
|
|
|
toptexture = TexMan(sidedef->GetTexture(side_t::top), true);
|
|
|
|
|
|
|
|
rw_offset_top = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top));
|
|
|
|
rowoffset = sidedef->GetTextureYOffset(side_t::top);
|
|
|
|
rw_toptexturescalex = sidedef->GetTextureXScale(side_t::top);
|
|
|
|
rw_toptexturescaley = sidedef->GetTextureYScale(side_t::top);
|
|
|
|
yrepeat = toptexture->Scale.Y * rw_toptexturescaley;
|
|
|
|
if (yrepeat >= 0)
|
|
|
|
{ // normal orientation
|
|
|
|
if (linedef->flags & ML_DONTPEGTOP)
|
|
|
|
{ // top of texture at top
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_toptexturemid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat;
|
2017-01-03 06:13:40 +00:00
|
|
|
if (rowoffset < 0 && toptexture != NULL)
|
|
|
|
{
|
|
|
|
rowoffset += toptexture->GetHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // bottom of texture at bottom
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_toptexturemid = (mBackSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight();
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // upside down
|
|
|
|
rowoffset = -rowoffset;
|
|
|
|
if (linedef->flags & ML_DONTPEGTOP)
|
|
|
|
{ // bottom of texture at top
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_toptexturemid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight();
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // top of texture at bottom
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_toptexturemid = (mBackSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (toptexture->bWorldPanning)
|
|
|
|
{
|
|
|
|
rw_toptexturemid += rowoffset * yrepeat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rw_toptexturemid += rowoffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rw_havelow)
|
|
|
|
{ // bottom texture
|
|
|
|
bottomtexture = TexMan(sidedef->GetTexture(side_t::bottom), true);
|
|
|
|
|
|
|
|
rw_offset_bottom = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::bottom));
|
|
|
|
rowoffset = sidedef->GetTextureYOffset(side_t::bottom);
|
|
|
|
rw_bottomtexturescalex = sidedef->GetTextureXScale(side_t::bottom);
|
|
|
|
rw_bottomtexturescaley = sidedef->GetTextureYScale(side_t::bottom);
|
|
|
|
yrepeat = bottomtexture->Scale.Y * rw_bottomtexturescaley;
|
|
|
|
if (yrepeat >= 0)
|
|
|
|
{ // normal orientation
|
|
|
|
if (linedef->flags & ML_DONTPEGBOTTOM)
|
|
|
|
{ // bottom of texture at bottom
|
|
|
|
rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // top of texture at top
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_bottomtexturemid = (mBackSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat;
|
2017-01-03 06:13:40 +00:00
|
|
|
if (rowoffset < 0 && bottomtexture != NULL)
|
|
|
|
{
|
|
|
|
rowoffset += bottomtexture->GetHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // upside down
|
|
|
|
rowoffset = -rowoffset;
|
|
|
|
if (linedef->flags & ML_DONTPEGBOTTOM)
|
|
|
|
{ // top of texture at bottom
|
|
|
|
rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // bottom of texture at top
|
2017-02-11 17:00:02 +00:00
|
|
|
rw_bottomtexturemid = (mBackSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + bottomtexture->GetHeight();
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bottomtexture->bWorldPanning)
|
|
|
|
{
|
|
|
|
rw_bottomtexturemid += rowoffset * yrepeat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rw_bottomtexturemid += rowoffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FTexture *midtex = TexMan(sidedef->GetTexture(side_t::mid), true);
|
|
|
|
|
|
|
|
bool segtextured = midtex != NULL || toptexture != NULL || bottomtexture != NULL;
|
|
|
|
|
|
|
|
// calculate light table
|
2017-02-11 17:00:02 +00:00
|
|
|
if (needlights && (segtextured || (mBackSector && IsFogBoundary(mFrontSector, mBackSector))))
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
lwallscale =
|
|
|
|
midtex ? (midtex->Scale.X * sidedef->GetTextureXScale(side_t::mid)) :
|
|
|
|
toptexture ? (toptexture->Scale.X * sidedef->GetTextureXScale(side_t::top)) :
|
|
|
|
bottomtexture ? (bottomtexture->Scale.X * sidedef->GetTextureXScale(side_t::bottom)) :
|
|
|
|
1.;
|
|
|
|
|
2017-01-24 05:50:17 +00:00
|
|
|
walltexcoords.Project(sidedef->TexelLength * lwallscale, WallC.sx1, WallC.sx2, WallT);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-01-26 09:22:54 +00:00
|
|
|
CameraLight *cameraLight = CameraLight::Instance();
|
2017-02-03 08:00:46 +00:00
|
|
|
if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
wallshade = LIGHT2SHADE(mLineSegment->sidedef->GetLightLevel(foggy, mFrontSector->lightlevel) + R_ActualExtraLight(foggy));
|
2017-01-26 08:49:07 +00:00
|
|
|
double GlobVis = LightVisibility::Instance()->WallGlobVis();
|
2017-01-03 06:13:40 +00:00
|
|
|
rw_lightleft = float(GlobVis / WallC.sz1);
|
|
|
|
rw_lightstep = float((GlobVis / WallC.sz2 - rw_lightleft) / (WallC.sx2 - WallC.sx1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rw_lightleft = 1;
|
|
|
|
rw_lightstep = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 18:08:02 +00:00
|
|
|
bool SWRenderLine::IsFogBoundary(sector_t *front, sector_t *back) const
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-02-03 08:00:46 +00:00
|
|
|
return r_fogboundary && CameraLight::Instance()->FixedColormap() == nullptr && front->ColorMap->Fade &&
|
2017-01-03 06:13:40 +00:00
|
|
|
front->ColorMap->Fade != back->ColorMap->Fade &&
|
|
|
|
(front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draws zero, one, or two textures for walls.
|
|
|
|
// Can draw or mark the starting pixel of floor and ceiling textures.
|
2017-01-03 18:08:02 +00:00
|
|
|
void SWRenderLine::RenderWallSegmentTextures(int x1, int x2)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
|
|
|
int x;
|
|
|
|
double xscale;
|
|
|
|
double yscale;
|
|
|
|
fixed_t xoffset = rw_offset;
|
|
|
|
|
2017-01-29 09:05:37 +00:00
|
|
|
WallDrawerArgs drawerargs;
|
2017-01-29 06:49:04 +00:00
|
|
|
|
2017-01-30 10:25:25 +00:00
|
|
|
drawerargs.SetStyle(false, false, OPAQUE);
|
2017-01-28 15:36:39 +00:00
|
|
|
|
2017-01-26 09:22:54 +00:00
|
|
|
CameraLight *cameraLight = CameraLight::Instance();
|
2017-02-03 08:00:46 +00:00
|
|
|
if (cameraLight->FixedLightLevel() >= 0)
|
|
|
|
drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel()));
|
|
|
|
else if (cameraLight->FixedColormap() != nullptr)
|
|
|
|
drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0);
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// clip wall to the floor and ceiling
|
2017-02-03 23:25:37 +00:00
|
|
|
auto ceilingclip = Thread->OpaquePass->ceilingclip;
|
|
|
|
auto floorclip = Thread->OpaquePass->floorclip;
|
2017-01-03 06:13:40 +00:00
|
|
|
for (x = x1; x < x2; ++x)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
if (walltop.ScreenY[x] < ceilingclip[x])
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
walltop.ScreenY[x] = ceilingclip[x];
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-01-24 05:50:17 +00:00
|
|
|
if (wallbottom.ScreenY[x] > floorclip[x])
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
wallbottom.ScreenY[x] = floorclip[x];
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-06 15:04:27 +00:00
|
|
|
Clip3DFloors *clip3d = Thread->Clip3D.get();
|
2017-01-04 17:54:14 +00:00
|
|
|
|
2017-01-03 06:13:40 +00:00
|
|
|
// mark ceiling areas
|
|
|
|
if (markceiling)
|
|
|
|
{
|
|
|
|
for (x = x1; x < x2; ++x)
|
|
|
|
{
|
2017-01-04 17:54:14 +00:00
|
|
|
short top = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKECEILING) ? clip3d->fakeFloor->ceilingclip[x] : ceilingclip[x];
|
2017-01-24 05:50:17 +00:00
|
|
|
short bottom = MIN(walltop.ScreenY[x], floorclip[x]);
|
2017-01-03 06:13:40 +00:00
|
|
|
if (top < bottom)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
mCeilingPlane->top[x] = top;
|
|
|
|
mCeilingPlane->bottom[x] = bottom;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mark floor areas
|
|
|
|
if (markfloor)
|
|
|
|
{
|
|
|
|
for (x = x1; x < x2; ++x)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
short top = MAX(wallbottom.ScreenY[x], ceilingclip[x]);
|
2017-01-04 17:54:14 +00:00
|
|
|
short bottom = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKEFLOOR) ? clip3d->fakeFloor->floorclip[x] : floorclip[x];
|
2017-01-03 06:13:40 +00:00
|
|
|
if (top < bottom)
|
|
|
|
{
|
|
|
|
assert(bottom <= viewheight);
|
2017-02-11 17:00:02 +00:00
|
|
|
mFloorPlane->top[x] = top;
|
|
|
|
mFloorPlane->bottom[x] = bottom;
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// kg3D - fake planes clipping
|
2017-01-04 17:54:14 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_REFRESHCLIP)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-04 17:54:14 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_CLIPBOTFRONT)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(clip3d->fakeFloor->floorclip + x1, wallbottom.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (x = x1; x < x2; ++x)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
walllower.ScreenY[x] = MIN(MAX(walllower.ScreenY[x], ceilingclip[x]), wallbottom.ScreenY[x]);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(clip3d->fakeFloor->floorclip + x1, walllower.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-01-04 17:54:14 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_CLIPTOPFRONT)
|
2017-01-03 06:13:40 +00:00
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(clip3d->fakeFloor->ceilingclip + x1, walltop.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (x = x1; x < x2; ++x)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
wallupper.ScreenY[x] = MAX(MIN(wallupper.ScreenY[x], floorclip[x]), walltop.ScreenY[x]);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(clip3d->fakeFloor->ceilingclip + x1, wallupper.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-04 17:54:14 +00:00
|
|
|
if (clip3d->fake3D & FAKE3D_FAKEMASK) return;
|
2017-01-03 06:13:40 +00:00
|
|
|
|
2017-02-11 17:00:02 +00:00
|
|
|
FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr;
|
2017-01-03 06:13:40 +00:00
|
|
|
|
|
|
|
// draw the wall tiers
|
|
|
|
if (midtexture)
|
|
|
|
{ // one sided line
|
|
|
|
if (midtexture->UseType != FTexture::TEX_Null && viewactive)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
FTexture *rw_pic = midtexture;
|
2017-01-03 06:13:40 +00:00
|
|
|
xscale = rw_pic->Scale.X * rw_midtexturescalex;
|
|
|
|
yscale = rw_pic->Scale.Y * rw_midtexturescaley;
|
|
|
|
if (xscale != lwallscale)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
walltexcoords.ProjectPos(mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT);
|
2017-01-03 06:13:40 +00:00
|
|
|
lwallscale = xscale;
|
|
|
|
}
|
|
|
|
if (midtexture->bWorldPanning)
|
|
|
|
{
|
|
|
|
rw_offset = xs_RoundToInt(rw_offset_mid * xscale);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rw_offset = rw_offset_mid;
|
|
|
|
}
|
|
|
|
if (xscale < 0)
|
|
|
|
{
|
|
|
|
rw_offset = -rw_offset;
|
|
|
|
}
|
2017-01-24 07:41:35 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderWallPart renderWallpart(Thread);
|
2017-02-11 17:00:02 +00:00
|
|
|
renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walltop.ScreenY, wallbottom.ScreenY, rw_midtexturemid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mFrontCeilingZ1, mFrontCeilingZ2), MIN(mFrontFloorZ1, mFrontFloorZ2), false, wallshade, rw_offset, rw_light, rw_lightstep, light_list, foggy, basecolormap);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
fillshort(ceilingclip + x1, x2 - x1, viewheight);
|
|
|
|
fillshort(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)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
wallupper.ScreenY[x] = MAX(MIN(wallupper.ScreenY[x], floorclip[x]), walltop.ScreenY[x]);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
if (viewactive)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
FTexture *rw_pic = toptexture;
|
2017-01-03 06:13:40 +00:00
|
|
|
xscale = rw_pic->Scale.X * rw_toptexturescalex;
|
|
|
|
yscale = rw_pic->Scale.Y * rw_toptexturescaley;
|
|
|
|
if (xscale != lwallscale)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
walltexcoords.ProjectPos(mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT);
|
2017-01-03 06:13:40 +00:00
|
|
|
lwallscale = xscale;
|
|
|
|
}
|
|
|
|
if (toptexture->bWorldPanning)
|
|
|
|
{
|
|
|
|
rw_offset = xs_RoundToInt(rw_offset_top * xscale);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rw_offset = rw_offset_top;
|
|
|
|
}
|
|
|
|
if (xscale < 0)
|
|
|
|
{
|
|
|
|
rw_offset = -rw_offset;
|
|
|
|
}
|
2017-01-24 07:41:35 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderWallPart renderWallpart(Thread);
|
2017-02-11 17:00:02 +00:00
|
|
|
renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walltop.ScreenY, wallupper.ScreenY, rw_toptexturemid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mFrontCeilingZ1, mFrontCeilingZ2), MIN(mBackCeilingZ1, mBackCeilingZ2), false, wallshade, rw_offset, rw_light, rw_lightstep, light_list, foggy, basecolormap);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(ceilingclip + x1, wallupper.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else if (markceiling)
|
|
|
|
{ // no top wall
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(ceilingclip + x1, walltop.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bottomtexture != NULL && bottomtexture->UseType != FTexture::TEX_Null)
|
|
|
|
{ // bottom wall
|
|
|
|
for (x = x1; x < x2; ++x)
|
|
|
|
{
|
2017-01-24 05:50:17 +00:00
|
|
|
walllower.ScreenY[x] = MIN(MAX(walllower.ScreenY[x], ceilingclip[x]), wallbottom.ScreenY[x]);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
if (viewactive)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
FTexture *rw_pic = bottomtexture;
|
2017-01-03 06:13:40 +00:00
|
|
|
xscale = rw_pic->Scale.X * rw_bottomtexturescalex;
|
|
|
|
yscale = rw_pic->Scale.Y * rw_bottomtexturescaley;
|
|
|
|
if (xscale != lwallscale)
|
|
|
|
{
|
2017-02-11 17:00:02 +00:00
|
|
|
walltexcoords.ProjectPos(mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT);
|
2017-01-03 06:13:40 +00:00
|
|
|
lwallscale = xscale;
|
|
|
|
}
|
|
|
|
if (bottomtexture->bWorldPanning)
|
|
|
|
{
|
|
|
|
rw_offset = xs_RoundToInt(rw_offset_bottom * xscale);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rw_offset = rw_offset_bottom;
|
|
|
|
}
|
|
|
|
if (xscale < 0)
|
|
|
|
{
|
|
|
|
rw_offset = -rw_offset;
|
|
|
|
}
|
2017-01-24 07:41:35 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderWallPart renderWallpart(Thread);
|
2017-02-11 17:00:02 +00:00
|
|
|
renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walllower.ScreenY, wallbottom.ScreenY, rw_bottomtexturemid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mBackFloorZ1, mBackFloorZ2), MIN(mFrontFloorZ1, mFrontFloorZ2), false, wallshade, rw_offset, rw_light, rw_lightstep, light_list, foggy, basecolormap);
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(floorclip + x1, walllower.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
else if (markfloor)
|
|
|
|
{ // no bottom wall
|
2017-01-24 05:50:17 +00:00
|
|
|
memcpy(floorclip + x1, wallbottom.ScreenY + x1, (x2 - x1) * sizeof(short));
|
2017-01-03 06:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rw_offset = xoffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-01-01 09:28:35 +00:00
|
|
|
// Transform and clip coordinates. Returns true if it was clipped away
|
2017-02-03 23:25:37 +00:00
|
|
|
bool FWallCoords::Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close)
|
2017-01-01 09:28:35 +00:00
|
|
|
{
|
|
|
|
tleft.X = float(pt1.X * ViewSin - pt1.Y * ViewCos);
|
|
|
|
tright.X = float(pt2.X * ViewSin - pt2.Y * ViewCos);
|
|
|
|
|
|
|
|
tleft.Y = float(pt1.X * ViewTanCos + pt1.Y * ViewTanSin);
|
|
|
|
tright.Y = float(pt2.X * ViewTanCos + pt2.Y * ViewTanSin);
|
2017-01-05 03:55:26 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderPortal *renderportal = thread->Portal.get();
|
2017-02-01 15:02:21 +00:00
|
|
|
auto viewport = RenderViewport::Instance();
|
2017-01-01 09:28:35 +00:00
|
|
|
|
2017-01-05 03:55:26 +00:00
|
|
|
if (renderportal->MirrorFlags & RF_XFLIP)
|
2017-01-01 09:28:35 +00:00
|
|
|
{
|
|
|
|
float t = -tleft.X;
|
|
|
|
tleft.X = -tright.X;
|
|
|
|
tright.X = t;
|
|
|
|
swapvalues(tleft.Y, tright.Y);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tleft.X >= -tleft.Y)
|
|
|
|
{
|
|
|
|
if (tleft.X > tleft.Y) return true; // left edge is off the right side
|
|
|
|
if (tleft.Y == 0) return true;
|
2017-02-01 15:02:21 +00:00
|
|
|
sx1 = xs_RoundToInt(viewport->CenterX + tleft.X * viewport->CenterX / tleft.Y);
|
2017-01-01 09:28:35 +00:00
|
|
|
sz1 = tleft.Y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (tright.X < -tright.Y) return true; // wall is off the left side
|
|
|
|
float den = tleft.X - tright.X - tright.Y + tleft.Y;
|
|
|
|
if (den == 0) return true;
|
|
|
|
sx1 = 0;
|
|
|
|
sz1 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X + tleft.Y) / den;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sz1 < too_close)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (tright.X <= tright.Y)
|
|
|
|
{
|
|
|
|
if (tright.X < -tright.Y) return true; // right edge is off the left side
|
|
|
|
if (tright.Y == 0) return true;
|
2017-02-01 15:02:21 +00:00
|
|
|
sx2 = xs_RoundToInt(viewport->CenterX + tright.X * viewport->CenterX / tright.Y);
|
2017-01-01 09:28:35 +00:00
|
|
|
sz2 = tright.Y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (tleft.X > tleft.Y) return true; // wall is off the right side
|
|
|
|
float den = tright.Y - tleft.Y - tright.X + tleft.X;
|
|
|
|
if (den == 0) return true;
|
|
|
|
sx2 = viewwidth;
|
|
|
|
sz2 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X - tleft.Y) / den;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sz2 < too_close || sx2 <= sx1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
void FWallTmapVals::InitFromWallCoords(RenderThread *thread, const FWallCoords *wallc)
|
2017-01-01 09:28:35 +00:00
|
|
|
{
|
|
|
|
const FVector2 *left = &wallc->tleft;
|
|
|
|
const FVector2 *right = &wallc->tright;
|
2017-01-05 03:55:26 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderPortal *renderportal = thread->Portal.get();
|
2017-01-01 09:28:35 +00:00
|
|
|
|
2017-01-05 03:55:26 +00:00
|
|
|
if (renderportal->MirrorFlags & RF_XFLIP)
|
2017-01-01 09:28:35 +00:00
|
|
|
{
|
|
|
|
swapvalues(left, right);
|
|
|
|
}
|
|
|
|
UoverZorg = left->X * centerx;
|
|
|
|
UoverZstep = -left->Y;
|
|
|
|
InvZorg = (left->X - right->X) * centerx;
|
|
|
|
InvZstep = right->Y - left->Y;
|
|
|
|
}
|
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
void FWallTmapVals::InitFromLine(RenderThread *thread, const DVector2 &left, const DVector2 &right)
|
2017-01-01 09:28:35 +00:00
|
|
|
{
|
|
|
|
// Coordinates should have already had viewx,viewy subtracted
|
|
|
|
|
|
|
|
double fullx1 = left.X * ViewSin - left.Y * ViewCos;
|
|
|
|
double fullx2 = right.X * ViewSin - right.Y * ViewCos;
|
|
|
|
double fully1 = left.X * ViewTanCos + left.Y * ViewTanSin;
|
|
|
|
double fully2 = right.X * ViewTanCos + right.Y * ViewTanSin;
|
2017-01-05 03:55:26 +00:00
|
|
|
|
2017-02-03 23:25:37 +00:00
|
|
|
RenderPortal *renderportal = thread->Portal.get();
|
2017-01-01 09:28:35 +00:00
|
|
|
|
2017-01-05 03:55:26 +00:00
|
|
|
if (renderportal->MirrorFlags & RF_XFLIP)
|
2017-01-01 09:28:35 +00:00
|
|
|
{
|
|
|
|
fullx1 = -fullx1;
|
|
|
|
fullx2 = -fullx2;
|
|
|
|
}
|
|
|
|
|
|
|
|
UoverZorg = float(fullx1 * centerx);
|
|
|
|
UoverZstep = float(-fully1);
|
|
|
|
InvZorg = float((fullx1 - fullx2) * centerx);
|
|
|
|
InvZstep = float(fully2 - fully1);
|
|
|
|
}
|
|
|
|
}
|