// 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(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 static void R_ClipWallSegment(int first, int last) { while (first < last) { UINT8 *p; if (solidcol[first]) { p = static_cast(memchr(solidcol+first, 0, last-first)); if (!p) return; // All solid first = p - solidcol; } else { p = static_cast(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 = §ors[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(x1, x2); if (g_walloffscreen) R_ClipWallSegment(x1, x2); return; clipsolid: g_walloffscreen = false; R_ClipWallSegment(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(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<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 = §ors[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(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 = §or->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 = §ors[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); }