Files
SRB2Kart-Saturn/src/r_bsp.cpp
T
2025-12-04 15:41:20 +01:00

1250 lines
35 KiB
C++

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file r_bsp.c
/// \brief BSP traversal, handling of LineSegs for rendering
#include "doomdef.h"
#include "g_game.h"
#include "r_fps.h"
#include "r_local.h"
#include "r_state.h"
#include "r_portal.h" // Add seg portals
#include "r_splats.h"
#include "p_local.h" // camera
#include "p_slopes.h"
#include "z_zone.h" // Check R_Prep3DFloors
#include "qs22j.h"
seg_t *curline = NULL;
side_t *sidedef = NULL;
line_t *linedef = NULL;
sector_t *frontsector = NULL;
sector_t *backsector = NULL;
// very ugly realloc() of drawsegs at run-time, I upped it to 512
// instead of 256.. and someone managed to send me a level with
// 896 drawsegs! So too bad here's a limit removal a-la-Boom
drawseg_t *curdrawsegs = NULL; /**< This is used to handle multiple lists for masked drawsegs. */
drawseg_t *drawsegs = NULL;
drawseg_t *ds_p = NULL;
// indicates doors closed wrt automap bugfix:
INT32 doorclosed = 0;
// A wall was drawn covering the whole screen, which means we
// can block off the BSP across that seg.
boolean g_walloffscreen = false;
boolean R_NoEncore(sector_t *sector, boolean ceiling)
{
const INT32 invspecial = GETSECSPECIAL(sector->special, 2);
boolean invertencore = (invspecial == 12 || invspecial == 15); // keeping this incase its used
#if 0 // perfect implementation
const INT32 sprnspecial = GETSECSPECIAL(sector->special, 3);
if (sprnspecial != 1 && sprnspecial != 3 // spring panel
#else // optimised, see #define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15)
if ((!(sector->special & (1<<8)) || (sector->special & ((4|8)<<8))) // spring panel
#endif
&& GETSECSPECIAL(sector->special, 4) != 6) // sneaker panel
{
return invertencore;
}
if (invertencore)
{
return false;
}
if (ceiling)
{
return ((boolean)(sector->flags & SF_FLIPSPECIAL_CEILING));
}
return ((boolean)(sector->flags & SF_FLIPSPECIAL_FLOOR));
}
//
// R_ClearDrawSegs
//
void R_ClearDrawSegs(void)
{
ds_p = drawsegs;
}
// CPhipps -
// Instead of clipsegs, let's try using an array with one entry for each column,
// indicating whether it's blocked by a solid wall yet or not.
UINT8 *solidcol = NULL;
void R_AllocClipSegMemory(void)
{
solidcol = static_cast<UINT8*>(Z_Realloc(solidcol, sizeof(*solidcol) * viewwidth, PU_STATIC, NULL));
}
namespace
{
enum class ClipType
{
// Does handle solid walls,
// e.g. single sided LineDefs (middle texture)
// that entirely block the view.
kSolid,
kSolidDontRender,
// Clips the given range of columns, but does not include it in the clip list.
// Does handle windows, e.g. LineDefs with upper and lower texture.
kPass,
};
// CPhipps -
// R_ClipWallSegment
//
// Replaces the old R_Clip*WallSegment functions. It draws bits of walls in those
// columns which aren't solid, and updates the solidcol[] array appropriately
template <ClipType Type>
static void R_ClipWallSegment(int first, int last)
{
while (first < last)
{
UINT8 *p;
if (solidcol[first])
{
p = static_cast<UINT8*>(memchr(solidcol+first, 0, last-first));
if (!p)
return; // All solid
first = p - solidcol;
}
else
{
p = static_cast<UINT8*>(memchr(solidcol+first, 1, last-first));
int to;
if (!p)
to = last;
else
to = p - solidcol;
if constexpr (Type != ClipType::kSolidDontRender)
{
R_StoreWallRange(first, to-1);
}
if constexpr (Type != ClipType::kPass)
{
memset(solidcol+first, 1, to-first);
}
first = to;
}
}
}
}; // namespace
//
// R_ClearClipSegs
//
void R_ClearClipSegs(void)
{
memset(solidcol, 0, viewwidth);
}
void R_PortalClearClipSegs(INT32 start, INT32 end)
{
R_ClearClipSegs();
if (start > 0)
memset(solidcol, 1, start);
if (end < viewwidth)
memset(solidcol + end, 1, viewwidth - end);
}
//
// If player's view height is underneath fake floor, lower the
// drawn ceiling to be just under the floor height, and replace
// the drawn floor and ceiling textures, and light level, with
// the control sector's.
//
// Similar for ceiling, only reflected.
//
sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
INT32 *ceilinglightlevel, boolean back)
{
if (floorlightlevel)
*floorlightlevel = sec->floorlightsec == -1 ?
sec->lightlevel : sectors[sec->floorlightsec].lightlevel;
if (ceilinglightlevel)
*ceilinglightlevel = sec->ceilinglightsec == -1 ?
sec->lightlevel : sectors[sec->ceilinglightsec].lightlevel;
// if (sec->midmap != -1)
// mapnum = sec->midmap;
// In original colormap code, this block did not run if sec->midmap was set
if (!sec->extra_colormap && sec->heightsec != -1)
{
const sector_t *s = &sectors[sec->heightsec];
mobj_t *pviewmobj = viewplayer->mo;
INT32 heightsec;
boolean underwater;
camera_t *thiscam = &camera[R_GetViewNumber()];
if (thiscam->chase)
heightsec = thiscam->subsector->sector->heightsec;
else if (viewmobj)
heightsec = pviewmobj->subsector->sector->heightsec;
else
return sec;
underwater = (heightsec != -1 && viewz <= sectors[heightsec].floorheight);
// Replace sector being drawn, with a copy to be hacked
*tempsec = *sec;
// Replace floor and ceiling height with other sector's heights.
tempsec->floorheight = s->floorheight;
tempsec->ceilingheight = s->ceilingheight;
if ((underwater && (tempsec-> floorheight = sec->floorheight,
tempsec->ceilingheight = s->floorheight - 1, !back)) || viewz <= s->floorheight)
{ // head-below-floor hack
tempsec->floorpic = s->floorpic;
tempsec->floor_xoffs = s->floor_xoffs;
tempsec->floor_yoffs = s->floor_yoffs;
tempsec->floorpic_angle = s->floorpic_angle;
if (underwater)
{
if (s->ceilingpic == skyflatnum)
{
tempsec->floorheight = tempsec->ceilingheight+1;
tempsec->ceilingpic = tempsec->floorpic;
tempsec->ceiling_xoffs = tempsec->floor_xoffs;
tempsec->ceiling_yoffs = tempsec->floor_yoffs;
tempsec->ceilingpic_angle = tempsec->floorpic_angle;
}
else
{
tempsec->ceilingpic = s->ceilingpic;
tempsec->ceiling_xoffs = s->ceiling_xoffs;
tempsec->ceiling_yoffs = s->ceiling_yoffs;
tempsec->ceilingpic_angle = s->ceilingpic_angle;
}
}
tempsec->lightlevel = s->lightlevel;
if (floorlightlevel)
*floorlightlevel = s->floorlightsec == -1 ? s->lightlevel
: sectors[s->floorlightsec].lightlevel;
if (ceilinglightlevel)
*ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel
: sectors[s->ceilinglightsec].lightlevel;
}
else if (heightsec != -1 && viewz >= sectors[heightsec].ceilingheight
&& sec->ceilingheight > s->ceilingheight)
{ // Above-ceiling hack
tempsec->ceilingheight = s->ceilingheight;
tempsec->floorheight = s->ceilingheight + 1;
tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic;
tempsec->floor_xoffs = tempsec->ceiling_xoffs = s->ceiling_xoffs;
tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs;
tempsec->floorpic_angle = tempsec->ceilingpic_angle = s->ceilingpic_angle;
if (s->floorpic == skyflatnum) // SKYFIX?
{
tempsec->ceilingheight = tempsec->floorheight-1;
tempsec->floorpic = tempsec->ceilingpic;
tempsec->floor_xoffs = tempsec->ceiling_xoffs;
tempsec->floor_yoffs = tempsec->ceiling_yoffs;
tempsec->floorpic_angle = tempsec->ceilingpic_angle;
}
else
{
tempsec->ceilingheight = sec->ceilingheight;
tempsec->floorpic = s->floorpic;
tempsec->floor_xoffs = s->floor_xoffs;
tempsec->floor_yoffs = s->floor_yoffs;
tempsec->floorpic_angle = s->floorpic_angle;
}
tempsec->lightlevel = s->lightlevel;
if (floorlightlevel)
*floorlightlevel = s->floorlightsec == -1 ? s->lightlevel :
sectors[s->floorlightsec].lightlevel;
if (ceilinglightlevel)
*ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel :
sectors[s->ceilinglightsec].lightlevel;
}
sec = tempsec;
}
return sec;
}
boolean R_IsEmptyLine(seg_t *line, const sector_t *front, const sector_t *back)
{
return (!line->sidedef->midtexture && !line->polyseg
&& ((!front->ffloors && !back->ffloors) || front->tag == back->tag)
&& (memcmp(front, back, (offsetof(sector_t, extra_colormap) + sizeof(extracolormap_t *))) == 0));
}
//
// R_AddLine
// Clips the given segment and adds any visible pieces to the line list.
//
static void R_AddLine(seg_t *line)
{
INT32 x1, x2;
angle_t angle1, angle2, span, tspan;
static sector_t tempsec;
boolean bothfloorssky = false;
g_portal = NULL;
// big room fix
angle1 = R_PointToAngle64(line->v1->x, line->v1->y);
angle2 = R_PointToAngle64(line->v2->x, line->v2->y);
curline = line;
// Clip to view edges.
span = angle1 - angle2;
// Back side? i.e. backface culling?
if (span >= ANGLE_180)
return;
// Global angle needed by segcalc.
rw_angle1 = angle1;
angle1 -= viewangle;
angle2 -= viewangle;
tspan = angle1 + clipangle;
if (tspan > doubleclipangle)
{
tspan -= doubleclipangle;
// Totally off the left edge?
if (tspan >= span)
return;
angle1 = clipangle;
}
tspan = clipangle - angle2;
if (tspan > doubleclipangle)
{
tspan -= doubleclipangle;
// Totally off the left edge?
if (tspan >= span)
return;
angle2 = -(signed)clipangle;
}
// The seg is in the view range, but not necessarily visible.
angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT;
angle2 = (angle2+ANGLE_90)>>ANGLETOFINESHIFT;
// killough 1/31/98: Here is where "slime trails" can SOMETIMES occur:
x1 = viewangletox[angle1];
x2 = viewangletox[angle2];
// Does not cross a pixel?
if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness
return;
backsector = line->backsector;
// Portal line
if (line->linedef->special == PORTALSPECIAL && line->side == 0)
{
if (portalrender < cv_maxportals.value)
{
// Find the other side!
INT32 line2 = P_FindSpecialLineFromTag(PORTALSPECIAL, line->linedef->tag, -1);
if (line->linedef == &lines[line2])
line2 = P_FindSpecialLineFromTag(PORTALSPECIAL, line->linedef->tag, line2);
if (line2 >= 0) // found it!
{
Portal_Add2Lines(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering
//return; // Don't fill in that space now!
goto clipsolid;
}
}
// Recursed TOO FAR (viewing a portal within a portal)
// So uhhh, render it as a normal wall instead or something ???
}
// Single sided line?
if (!backsector)
goto clipsolid;
backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true);
doorclosed = 0;
// likewise, but for floors and upper textures
if (backsector->floorpic == skyflatnum && frontsector->floorpic == skyflatnum)
bothfloorssky = true;
fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends
fixed_t backf1, backf2, backc1, backc2; // back floor ceiling ends
// Closed door.
if (frontsector->f_slope || frontsector->c_slope || backsector->f_slope || backsector->c_slope)
{
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
end1 = P_GetZAt(slope, line->v1->x, line->v1->y, normalheight); \
end2 = P_GetZAt(slope, line->v2->x, line->v2->y, normalheight);
SLOPEPARAMS(frontsector->f_slope, frontf1, frontf2, frontsector->floorheight)
SLOPEPARAMS(frontsector->c_slope, frontc1, frontc2, frontsector->ceilingheight)
SLOPEPARAMS( backsector->f_slope, backf1, backf2, backsector->floorheight)
SLOPEPARAMS( backsector->c_slope, backc1, backc2, backsector->ceilingheight)
#undef SLOPEPARAMS
}
else
{
frontf1 = frontf2 = frontsector->floorheight;
frontc1 = frontc2 = frontsector->ceilingheight;
backf1 = backf2 = backsector->floorheight;
backc1 = backc2 = backsector->ceilingheight;
}
if (viewsector != backsector && viewsector != frontsector)
{
// here we're talking about a CEILING lower than a floor. ...yeah we don't even need to bother.
const boolean ceillow = (backc1 <= frontf1 && backc2 <= frontf2);
// this tries to fix some skyboxes drawing a black void as a midtexture lel
if (ceillow && (!bothfloorssky && backsector->ceilingpic == skyflatnum && frontsector->ceilingpic != skyflatnum) // double sided with back ceiling sky but not front ceiling sky
&& !line->sidedef->midtexture && !line->sidedef->bottomtexture && !line->sidedef->toptexture && !line->polyseg) // effectively empty
{
goto clippass;
}
doorclosed = ceillow
|| (backf1 >= frontc1 && backf2 >= frontc2)
// Check for automap fix. Store in doorclosed for r_segs.c
|| (backc1 <= backf1 && backc2 <= backf2
&& ((backc1 >= frontc1 && backc2 >= frontc2) || curline->sidedef->toptexture)
&& ((backf1 <= frontf1 && backf2 >= frontf2) || curline->sidedef->bottomtexture)
&& (backsector->ceilingpic != skyflatnum || frontsector->ceilingpic != skyflatnum));
if (doorclosed)
goto clipsolid;
}
// Window.
if (backc1 != frontc1 || backc2 != frontc2
|| backf1 != frontf1 || backf2 != frontf2)
{
goto clippass;
}
// 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.
if (R_IsEmptyLine(line, frontsector, backsector))
return;
clippass:
g_walloffscreen = false;
R_ClipWallSegment<ClipType::kPass>(x1, x2);
if (g_walloffscreen)
R_ClipWallSegment<ClipType::kSolidDontRender>(x1, x2);
return;
clipsolid:
g_walloffscreen = false;
R_ClipWallSegment<ClipType::kSolid>(x1, x2);
}
//
// R_CheckBBox
// Checks BSP node/subtree bounding box.
// Returns true if some part of the bbox might be visible.
//
// | 0 | 1 | 2
// --+---+---+---
// 0 | 0 | 1 | 2
// 1 | 4 | 5 | 6
// 2 | 8 | 9 | A
const INT32 checkcoord[12][4] =
{
{3, 0, 2, 1},
{3, 0, 2, 0},
{3, 1, 2, 0},
{0}, // UNUSED
{2, 0, 2, 1},
{0}, // UNUSED
{3, 1, 3, 0},
{0}, // UNUSED
{2, 0, 3, 1},
{2, 1, 3, 1},
{2, 1, 3, 0}
};
static boolean R_CheckBBox(const fixed_t *bspcoord)
{
angle_t angle1, angle2;
INT32 sx1, sx2;
const INT32* check;
// Find the corners of the box
// that define the edges from current viewpoint.
const INT32 boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 :
viewx < bspcoord[BOXRIGHT] ? 1 : 2) +
(viewy >= bspcoord[BOXTOP] ? 0 :
viewy > bspcoord[BOXBOTTOM] ? 4 : 8);
if (boxpos == 5)
return true;
check = checkcoord[boxpos];
// big room fix
angle1 = R_PointToAngle64(bspcoord[check[0]], bspcoord[check[1]]) - viewangle;
angle2 = R_PointToAngle64(bspcoord[check[2]], bspcoord[check[3]]) - viewangle;
// cph - replaced old code, which was unclear and badly commented
// Much more efficient code now
if ((signed)angle1 < (signed)angle2) /* it's "behind" us */
{
/* Either angle1 or angle2 is behind us, so it doesn't matter if we
* change it to the corect sign
*/
if ((angle1 >= ANGLE_180) && (angle1 < ANGLE_270))
angle1 = ANGLE_180-1;
else
angle2 = ANGLE_180;
}
if ((signed)angle2 >= (signed)clipangle) return false; // Both off left edge
if ((signed)angle1 <= -(signed)clipangle) return false; // Both off right edge
if ((signed)angle1 >= (signed)clipangle) angle1 = clipangle; // Clip at left edge
if ((signed)angle2 <= -(signed)clipangle) angle2 = -(signed)clipangle; // Clip at right edge
// Find the first clippost that touches the source post (adjacent pixels are touching).
angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT;
angle2 = (angle2+ANGLE_90)>>ANGLETOFINESHIFT;
sx1 = viewangletox[angle1];
sx2 = viewangletox[angle2];
// Does not cross a pixel.
if (sx1 >= sx2)
return false;
if (!memchr(solidcol+sx1, 0, sx2-sx1))
{
// All columns it covers are already solidly covered
return false;
}
return true;
}
size_t numpolys; // number of polyobjects in current subsector
size_t num_po_ptrs; // number of polyobject pointers allocated
polyobj_t **po_ptrs; // temp ptr array to sort polyobject pointers
//
// R_PolyobjCompare
//
// Callback for qsort that compares the z distance of two polyobjects.
// Returns the difference such that the closer polyobject will be
// sorted first.
//
static int R_PolyobjCompare(const void *p1, const void *p2)
{
const polyobj_t *po1 = *(const polyobj_t * const *)p1;
const polyobj_t *po2 = *(const polyobj_t * const *)p2;
return po1->zdist - po2->zdist;
}
//
// R_SortPolyObjects
//
// haleyjd 03/03/06: Here's the REAL meat of Eternity's polyobject system.
// Hexen just figured this was impossible, but as mentioned in polyobj.c,
// it is perfectly doable within the confines of the BSP tree. Polyobjects
// must be sorted to draw in DOOM's front-to-back order within individual
// subsectors. This is a modified version of R_SortVisSprites.
//
void R_SortPolyObjects(subsector_t *sub)
{
if (numpolys)
{
polyobj_t *po;
INT32 i = 0;
// allocate twice the number needed to minimize allocations
if (num_po_ptrs < numpolys*2)
{
// use free instead realloc since faster (thanks Lee ^_^)
free(po_ptrs);
po_ptrs = static_cast<polyobj_t**>(malloc((num_po_ptrs = numpolys*2)* sizeof(*po_ptrs)));
}
po = sub->polyList;
while (po)
{
po->zdist = R_PointToDist2(viewx, viewy,
po->centerPt.x, po->centerPt.y);
po_ptrs[i++] = po;
po = (polyobj_t *)(po->link.next);
}
// the polyobjects are NOT in any particular order, so use qsort
// 03/10/06: only bother if there are actually polys to sort
if (numpolys >= 2)
{
qs22j(po_ptrs, numpolys, sizeof(polyobj_t *), R_PolyobjCompare);
}
}
}
//
// R_PolysegCompare
//
// Callback for qsort to sort the segs of a polyobject. Returns such that the
// closer one is sorted first. I sure hope this doesn't break anything. -Red
//
static int R_PolysegCompare(const void *p1, const void *p2)
{
const seg_t *seg1 = *(const seg_t * const *)p1;
const seg_t *seg2 = *(const seg_t * const *)p2;
fixed_t dist1v1, dist1v2, dist2v1, dist2v2;
// TODO might be a better way to get distance?
#define pdist(x, y) (FixedMul(R_PointToDist(x, y), FINECOSINE((R_PointToAngle(x, y)-viewangle)>>ANGLETOFINESHIFT))+0xFFFFFFF)
#define vxdist(v) pdist(v->x, v->y)
dist1v1 = vxdist(seg1->v1);
dist1v2 = vxdist(seg1->v2);
dist2v1 = vxdist(seg2->v1);
dist2v2 = vxdist(seg2->v2);
if (std::min(dist1v1, dist1v2) != std::min(dist2v1, dist2v2))
return std::min(dist1v1, dist1v2) - std::min(dist2v1, dist2v2);
{ // That didn't work, so now let's try this.......
fixed_t delta1, delta2, x1, y1, x2, y2;
vertex_t *near1, *near2, *far1, *far2; // wherever you are~
delta1 = R_PointToDist2(seg1->v1->x, seg1->v1->y, seg1->v2->x, seg1->v2->y);
delta2 = R_PointToDist2(seg2->v1->x, seg2->v1->y, seg2->v2->x, seg2->v2->y);
delta1 = FixedDiv(128<<FRACBITS, delta1);
delta2 = FixedDiv(128<<FRACBITS, delta2);
if (dist1v1 < dist1v2)
{
near1 = seg1->v1;
far1 = seg1->v2;
}
else
{
near1 = seg1->v2;
far1 = seg1->v1;
}
if (dist2v1 < dist2v2)
{
near2 = seg2->v1;
far2 = seg2->v2;
}
else
{
near2 = seg2->v2;
far2 = seg2->v1;
}
x1 = near1->x + FixedMul(far1->x-near1->x, delta1);
y1 = near1->y + FixedMul(far1->y-near1->y, delta1);
x2 = near2->x + FixedMul(far2->x-near2->x, delta2);
y2 = near2->y + FixedMul(far2->y-near2->y, delta2);
return pdist(x1, y1)-pdist(x2, y2);
}
#undef vxdist
#undef pdist
}
//
// R_AddPolyObjects
//
// haleyjd 02/19/06
// Adds all segs in all polyobjects in the given subsector.
//
static void R_AddPolyObjects(subsector_t *sub)
{
polyobj_t *po = sub->polyList;
size_t i, j;
numpolys = 0;
// count polyobjects
while (po)
{
++numpolys;
po = (polyobj_t *)(po->link.next);
}
g_portal = NULL;
// for render stats
ps_numpolyobjects.value.i += numpolys;
// sort polyobjects
R_SortPolyObjects(sub);
// render polyobjects
for (i = 0; i < numpolys; ++i)
{
qs22j(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare);
for (j = 0; j < po_ptrs[i]->segCount; ++j)
{
seg_t *seg = po_ptrs[i]->segs[j];
if (!(seg->polyseg->flags & POF_RENDERSIDES))
continue;
R_AddLine(seg);
}
}
}
//
// R_Subsector
// Determine floor/ceiling planes.
// Add sprites of things in sector.
// Draw one or more line segments.
//
drawseg_t *firstseg;
static void R_Subsector(size_t num)
{
INT32 count, floorlightlevel, ceilinglightlevel, light;
seg_t *line;
subsector_t *sub;
static sector_t tempsec; // Deep water hack
extracolormap_t *floorcolormap;
extracolormap_t *ceilingcolormap;
fixed_t floorcenterz, ceilingcenterz;
ffloor_t *rover;
// subsectors added at run-time
if (num >= numsubsectors)
{
#ifdef RANGECHECK
I_Error("R_Subsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors));
#else
return;
#endif
}
sub = &subsectors[num];
frontsector = sub->sector;
count = sub->numlines;
line = &segs[sub->firstline];
// Deep water/fake ceiling effect.
frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, false);
floorcolormap = ceilingcolormap = frontsector->extra_colormap;
floorcenterz = P_GetSectorFloorZAt(frontsector, frontsector->soundorg.x, frontsector->soundorg.y);
ceilingcenterz = P_GetSectorCeilingZAt(frontsector, frontsector->soundorg.x, frontsector->soundorg.y);
// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
if (frontsector->ffloors)
{
boolean anyMoved = frontsector->moved;
if (anyMoved == false)
{
for (rover = frontsector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES))
continue;
const sector_t *controlSec = &sectors[rover->secnum];
if (controlSec->moved != true)
continue;
anyMoved = true;
break;
}
}
if (anyMoved == true)
{
frontsector->numlights = sub->sector->numlights = 0;
R_Prep3DFloors(frontsector, ceilingcenterz);
sub->sector->lightlist = frontsector->lightlist;
sub->sector->numlights = frontsector->numlights;
sub->sector->moved = frontsector->moved = false;
}
light = R_GetPlaneLight(frontsector, floorcenterz, false);
if (frontsector->floorlightsec == -1)
floorlightlevel = *frontsector->lightlist[light].lightlevel;
floorcolormap = frontsector->lightlist[light].extra_colormap;
light = R_GetPlaneLight(frontsector, ceilingcenterz, false);
if (frontsector->ceilinglightsec == -1)
ceilinglightlevel = *frontsector->lightlist[light].lightlevel;
ceilingcolormap = frontsector->lightlist[light].extra_colormap;
}
sub->sector->extra_colormap = frontsector->extra_colormap;
if (P_GetSectorFloorZAt(frontsector, viewx, viewy) < viewz
|| frontsector->floorpic == skyflatnum
|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum))
{
floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel,
frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL
, NULL
, frontsector->f_slope
, R_NoEncore(frontsector, false), false, frontsector);
}
else
floorplane = NULL;
if (P_GetSectorCeilingZAt(frontsector, viewx, viewy) > viewz
|| frontsector->ceilingpic == skyflatnum
|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum))
{
ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic,
ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle,
ceilingcolormap, NULL
, NULL
, frontsector->c_slope
, R_NoEncore(frontsector, true), true, frontsector);
}
else
ceilingplane = NULL;
numffloors = 0;
visffloor[numffloors].slope = NULL;
visffloor[numffloors].plane = NULL;
visffloor[numffloors].polyobj = NULL;
if (frontsector->ffloors)
{
fixed_t heightcheck, planecenterz;
for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || !(rover->flags & FF_RENDERALL))
continue;
if (frontsector->cullheight)
{
if (R_DoCulling(frontsector->cullheight, viewsector->cullheight, viewz, *rover->bottomheight, *rover->topheight))
{
rover->norender = leveltime;
continue;
}
}
visffloor[numffloors].plane = NULL;
visffloor[numffloors].polyobj = NULL;
heightcheck = P_GetFFloorBottomZAt(rover, viewx, viewy);
planecenterz = P_GetFFloorBottomZAt(rover, frontsector->soundorg.x, frontsector->soundorg.y);
if (planecenterz <= ceilingcenterz
&& planecenterz >= floorcenterz
&& ((viewz < heightcheck && !(rover->flags & FF_INVERTPLANES))
|| (viewz > heightcheck && (rover->flags & FF_BOTHPLANES))))
{
light = R_GetPlaneLight(frontsector, planecenterz,
viewz < heightcheck);
visffloor[numffloors].plane = R_FindPlane(
*rover->bottomheight, *rover->bottompic,
*frontsector->lightlist[light].lightlevel,
*rover->bottomxoffs, *rover->bottomyoffs, *rover->bottomangle,
frontsector->lightlist[light].extra_colormap, rover
, NULL
, *rover->b_slope
, R_NoEncore(rover->master->frontsector, true), true, frontsector
);
visffloor[numffloors].slope = *rover->b_slope;
// Tell the renderer this sector has slopes in it.
if (visffloor[numffloors].slope)
frontsector->hasslope = true;
visffloor[numffloors].height = heightcheck;
visffloor[numffloors].ffloor = rover;
numffloors++;
}
if (numffloors >= MAXFFLOORS)
break;
visffloor[numffloors].plane = NULL;
visffloor[numffloors].polyobj = NULL;
heightcheck = P_GetFFloorTopZAt(rover, viewx, viewy);
planecenterz = P_GetFFloorTopZAt(rover, frontsector->soundorg.x, frontsector->soundorg.y);
if (planecenterz >= floorcenterz
&& planecenterz <= ceilingcenterz
&& ((viewz > heightcheck && !(rover->flags & FF_INVERTPLANES))
|| (viewz < heightcheck && (rover->flags & FF_BOTHPLANES))))
{
light = R_GetPlaneLight(frontsector, planecenterz, viewz < heightcheck);
visffloor[numffloors].plane = R_FindPlane(
*rover->topheight, *rover->toppic,
*frontsector->lightlist[light].lightlevel,
*rover->topxoffs, *rover->topyoffs, *rover->topangle,
frontsector->lightlist[light].extra_colormap, rover
, NULL
, *rover->t_slope
, R_NoEncore(rover->master->frontsector, false), false, frontsector
);
visffloor[numffloors].slope = *rover->t_slope;
// Tell the renderer this sector has slopes in it.
if (visffloor[numffloors].slope)
frontsector->hasslope = true;
visffloor[numffloors].height = heightcheck;
visffloor[numffloors].ffloor = rover;
numffloors++;
}
}
}
// Polyobjects have planes, too!
if (sub->polyList)
{
polyobj_t *po = sub->polyList;
sector_t *polysec;
while (po)
{
if (numffloors >= MAXFFLOORS)
break;
if (!(po->flags & POF_RENDERPLANES)) // Don't draw planes
{
po = (polyobj_t *)(po->link.next);
continue;
}
polysec = po->lines[0]->backsector;
visffloor[numffloors].plane = NULL;
if (polysec->floorheight <= ceilingcenterz
&& polysec->floorheight >= floorcenterz
&& (viewz < polysec->floorheight))
{
light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
visffloor[numffloors].plane = R_FindPlane(polysec->floorheight, polysec->floorpic,
(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->floor_xoffs, polysec->floor_yoffs,
polysec->floorpic_angle-po->angle,
(light == -1 ? frontsector->extra_colormap : frontsector->lightlist[light].extra_colormap), NULL, po
,NULL // will ffloors be slopable eventually?
, R_NoEncore(polysec, false), false, frontsector);
visffloor[numffloors].height = polysec->floorheight;
visffloor[numffloors].polyobj = po;
visffloor[numffloors].slope = NULL;
// visffloor[numffloors].ffloor = rover;
po->visplane = visffloor[numffloors].plane;
numffloors++;
}
if (numffloors >= MAXFFLOORS)
break;
visffloor[numffloors].plane = NULL;
if (polysec->ceilingheight >= floorcenterz
&& polysec->ceilingheight <= ceilingcenterz
&& (viewz > polysec->ceilingheight))
{
light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
visffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle,
(light == -1 ? frontsector->extra_colormap : frontsector->lightlist[light].extra_colormap), NULL, po
,NULL // will ffloors be slopable eventually?
, R_NoEncore(polysec, true), false, frontsector);
visffloor[numffloors].polyobj = po;
visffloor[numffloors].height = polysec->ceilingheight;
visffloor[numffloors].slope = NULL;
// visffloor[numffloors].ffloor = rover;
po->visplane = visffloor[numffloors].plane;
numffloors++;
}
po = (polyobj_t *)(po->link.next);
}
}
#ifdef FLOORSPLATS
if (sub->splats)
R_AddVisibleFloorSplats(sub);
#endif
// killough 9/18/98: Fix underwater slowdown, by passing real sector
// instead of fake one. Improve sprite lighting by basing sprite
// lightlevels on floor & ceiling lightlevels in the surrounding area.
//
// 10/98 killough:
//
// NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!!
// That is part of the 242 effect!!! If you simply pass sub->sector to
// the old code you will not get correct lighting for underwater sprites!!!
// Either you must pass the fake sector and handle validcount here, on the
// real sector, or you must account for the lighting in some other way,
// like passing it as an argument.
R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2);
firstseg = NULL;
// haleyjd 02/19/06: draw polyobjects before static lines
if (sub->polyList)
R_AddPolyObjects(sub);
while (count--)
{
// CONS_Debug(DBG_GAMELOGIC, "Adding normal line %d...(%d)\n", line->linedef-lines, leveltime);
if (!line->polyseg) // ignore segs that belong to polyobjects
R_AddLine(line);
line++;
curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so stuff doesn't try using it for other things */
}
}
//
// R_Prep3DFloors
//
// This function creates the lightlists that the given sector uses to light
// floors/ceilings/walls according to the 3D floors.
void R_Prep3DFloors(sector_t *sector, fixed_t secceilz)
{
ffloor_t *rover;
ffloor_t *best;
fixed_t bestheight, maxheight;
INT32 count, i;
sector_t *sec;
pslope_t *bestslope = NULL;
fixed_t heighttest; // I think it's better to check the Z height at the sector's center
// than assume unsloped heights are accurate indicators of order in sloped sectors. -Red
count = 1;
for (rover = sector->ffloors; rover; rover = rover->next)
{
if ((rover->flags & FF_EXISTS) && (!(rover->flags & FF_NOSHADE)
|| (rover->flags & FF_CUTLEVEL) || (rover->flags & FF_CUTSPRITES)))
{
count++;
if (rover->flags & FF_DOUBLESHADOW)
count++;
}
}
if (count != sector->numlights)
{
Z_Free(sector->lightlist);
sector->lightlist = static_cast<lightlist_t*>(Z_Malloc(sizeof(*sector->lightlist) * count, PU_LEVEL, NULL));
sector->numlights = count;
}
memset(sector->lightlist, 0, sizeof(lightlist_t) * count);
heighttest = secceilz; // sector ceiling z
sector->lightlist[0].height = heighttest + 1;
sector->lightlist[0].slope = sector->c_slope;
sector->lightlist[0].lightlevel = &sector->lightlevel;
sector->lightlist[0].caster = NULL;
sector->lightlist[0].extra_colormap = sector->extra_colormap;
sector->lightlist[0].flags = 0;
maxheight = INT32_MAX;
for (i = 1; i < count; i++)
{
bestheight = INT32_MAX * -1;
best = NULL;
for (rover = sector->ffloors; rover; rover = rover->next)
{
rover->lastlight = 0;
if (!(rover->flags & FF_EXISTS) || (rover->flags & FF_NOSHADE
&& !(rover->flags & FF_CUTLEVEL) && !(rover->flags & FF_CUTSPRITES)))
continue;
heighttest = P_GetFFloorTopZAt(rover, sector->soundorg.x, sector->soundorg.y);
if (heighttest > bestheight && heighttest < maxheight)
{
best = rover;
bestheight = heighttest;
bestslope = *rover->t_slope;
continue;
}
if (rover->flags & FF_DOUBLESHADOW)
{
heighttest = P_GetFFloorBottomZAt(rover, sector->soundorg.x, sector->soundorg.y);
if (heighttest > bestheight
&& heighttest < maxheight)
{
best = rover;
bestheight = heighttest;
bestslope = *rover->b_slope;
continue;
}
}
}
if (!best)
{
sector->numlights = i;
return;
}
sector->lightlist[i].height = maxheight = bestheight;
sector->lightlist[i].caster = best;
sector->lightlist[i].flags = best->flags;
sector->lightlist[i].slope = bestslope;
sec = &sectors[best->secnum];
if (best->flags & FF_NOSHADE)
{
sector->lightlist[i].lightlevel = sector->lightlist[i-1].lightlevel;
sector->lightlist[i].extra_colormap = sector->lightlist[i-1].extra_colormap;
}
else if (best->flags & FF_COLORMAPONLY)
{
sector->lightlist[i].lightlevel = sector->lightlist[i-1].lightlevel;
sector->lightlist[i].extra_colormap = sec->extra_colormap;
}
else
{
sector->lightlist[i].lightlevel = best->toplightlevel;
sector->lightlist[i].extra_colormap = sec->extra_colormap;
}
if (best->flags & FF_DOUBLESHADOW)
{
heighttest = P_GetFFloorBottomZAt(best, sector->soundorg.x, sector->soundorg.y);
if (bestheight == heighttest) ///TODO: do this in a more efficient way -Red
{
sector->lightlist[i].lightlevel = sector->lightlist[best->lastlight].lightlevel;
sector->lightlist[i].extra_colormap =
sector->lightlist[best->lastlight].extra_colormap;
}
else
best->lastlight = i - 1;
}
}
}
INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside)
{
INT32 i;
if (!underside)
{
for (i = 1; i < sector->numlights; i++)
if (sector->lightlist[i].height <= planeheight)
return i - 1;
return sector->numlights - 1;
}
for (i = 1; i < sector->numlights; i++)
if (sector->lightlist[i].height < planeheight)
return i - 1;
return sector->numlights - 1;
}
//
// RenderBSPNode
// Renders all subsectors below a given node,
// traversing subtree recursively.
// Just call with BSP root.
//
// killough 5/2/98: reformatted, removed tail recursion
void R_RenderBSPNode(INT32 bspnum)
{
const node_t *bsp;
INT32 side;
ps_numbspcalls.value.i++;
while (!(bspnum & NF_SUBSECTOR)) // Found a subsector?
{
bsp = &nodes[bspnum];
// Decide which side the view point is on.
side = R_PointOnSideFast(viewx, viewy, bsp);
// Recursively divide front space.
R_RenderBSPNode(bsp->children[side]);
// Possibly divide back space.
if (!R_CheckBBox(bsp->bbox[side^1]))
return;
bspnum = bsp->children[side^1];
}
// PORTAL CULLING
if (portalcullsector)
{
if (subsectors[bspnum & ~NF_SUBSECTOR].sector != portalcullsector)
return;
portalcullsector = NULL;
}
R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
}