From 1fb70c7d121fe05a8b4e30d98ecdd6e2951a4b9d Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Mon, 14 Jun 2021 21:36:32 -0300 Subject: [PATCH] Implement dynasegs, from Eternity --- src/CMakeLists.txt | 4 + src/Makefile | 2 + src/hardware/hw_bsp.c | 2 +- src/hardware/hw_main.c | 11 +- src/m_dllist.h | 7 +- src/p_maputl.c | 31 ++ src/p_maputl.h | 1 + src/p_mobj.c | 2 +- src/p_polyobj.c | 30 +- src/p_polyobj.h | 16 +- src/p_setup.c | 72 +++- src/p_setup.h | 2 + src/p_spec.c | 2 +- src/r_bsp.c | 133 +++---- src/r_defs.h | 36 +- src/r_dynabsp.c | 719 ++++++++++++++++++++++++++++++++++++++ src/r_dynabsp.h | 62 ++++ src/r_dynseg.c | 761 +++++++++++++++++++++++++++++++++++++++++ src/r_dynseg.h | 117 +++++++ src/r_segs.c | 13 +- src/r_state.h | 1 + 21 files changed, 1883 insertions(+), 141 deletions(-) create mode 100644 src/r_dynabsp.c create mode 100644 src/r_dynabsp.h create mode 100644 src/r_dynseg.c create mode 100644 src/r_dynseg.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 33898c9a1..eadc05a17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -131,6 +131,8 @@ set(SRB2_CORE_RENDER_SOURCES r_bsp.c r_data.c r_draw.c + r_dynabsp.c + r_dynseg.c r_main.c r_plane.c r_segs.c @@ -148,6 +150,8 @@ set(SRB2_CORE_RENDER_SOURCES r_data.h r_defs.h r_draw.h + r_dynabsp.h + r_dynseg.h r_local.h r_main.h r_plane.h diff --git a/src/Makefile b/src/Makefile index 1d3551ce2..94bfa5326 100644 --- a/src/Makefile +++ b/src/Makefile @@ -531,6 +531,8 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_bsp.o \ $(OBJDIR)/r_data.o \ $(OBJDIR)/r_draw.o \ + $(OBJDIR)/r_dynabsp.o\ + $(OBJDIR)/r_dynseg.o \ $(OBJDIR)/r_main.o \ $(OBJDIR)/r_plane.o \ $(OBJDIR)/r_segs.o \ diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c index 4db69ff8b..9737f28ce 100644 --- a/src/hardware/hw_bsp.c +++ b/src/hardware/hw_bsp.c @@ -444,7 +444,7 @@ static poly_t *CutOutSubsecPoly(seg_t *lseg, INT32 count, poly_t *poly) { line_t *line = lseg->linedef; - if (lseg->glseg) + if (lseg->glseg || lseg->polyseg) continue; //x,y,dx,dy (like a divline) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index d413e3bbe..062eb4722 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -28,6 +28,7 @@ #include "../r_patch.h" #include "../r_picformats.h" #include "../r_bsp.h" +#include "../r_dynseg.h" #include "../d_clisrv.h" #include "../w_wad.h" #include "../z_zone.h" @@ -2677,10 +2678,10 @@ static inline void HWR_AddPolyObjectSegs(void) M_Memcpy(gl_fakeline, po_ptrs[i]->segs[j], sizeof(seg_t)); // Now convert the line to float and add it to be rendered - pv1->x = FIXED_TO_FLOAT(gl_fakeline->v1->x); - pv1->y = FIXED_TO_FLOAT(gl_fakeline->v1->y); - pv2->x = FIXED_TO_FLOAT(gl_fakeline->v2->x); - pv2->y = FIXED_TO_FLOAT(gl_fakeline->v2->y); + pv1->x = FIXED_TO_FLOAT(gl_fakeline->dyv1->x); + pv1->y = FIXED_TO_FLOAT(gl_fakeline->dyv1->y); + pv2->x = FIXED_TO_FLOAT(gl_fakeline->dyv2->x); + pv2->y = FIXED_TO_FLOAT(gl_fakeline->dyv2->y); gl_fakeline->pv1 = pv1; gl_fakeline->pv2 = pv2; @@ -3227,7 +3228,7 @@ static void HWR_Subsector(size_t num) numpolys = 0; - // Count all the polyobjects, reset the list, and recount them + // Count all the polyobjects while (po) { ++numpolys; diff --git a/src/m_dllist.h b/src/m_dllist.h index 65303b4a3..840d2a803 100644 --- a/src/m_dllist.h +++ b/src/m_dllist.h @@ -50,8 +50,13 @@ FUNCINLINE static ATTRINLINE void M_DLListRemove(mdllistitem_t *item) mdllistitem_t **prev = item->prev; mdllistitem_t *next = item->next; - if ((*prev = next)) + // haleyjd 05/07/13: safety #1: only if prev is non-null + if (prev && (*prev = next)) next->prev = prev; + + // haleyjd 05/07/13: safety #2: clear links. + item->prev = NULL; + item->next = NULL; } #endif diff --git a/src/p_maputl.c b/src/p_maputl.c index efcebe736..29d5d0862 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -241,6 +241,37 @@ static INT32 P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t *line) return 1; // back side } +INT32 P_PointOnDivlineSidePrecise(fixed_t x, fixed_t y, const divline_t *line) +{ + fixed_t dx, dy; + INT64 left, right; + + if (!line->dx) + { + if (x <= line->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->x); + dy = (y - line->y); + + left = line->dy * dx; + right = dy * line->dx; + + if (right < left) + return 0; // front side + return 1; // back side +} + // // P_MakeDivline // diff --git a/src/p_maputl.h b/src/p_maputl.h index cec344d03..ad573d547 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -45,6 +45,7 @@ FUNCMATH fixed_t P_AproxDistance(fixed_t dx, fixed_t dy); void P_ClosestPointOnLine(fixed_t x, fixed_t y, line_t *line, vertex_t *result); void P_ClosestPointOnLine3D(const vector3_t *p, const vector3_t *line, vector3_t *result); INT32 P_PointOnLineSide(fixed_t x, fixed_t y, line_t *line); +INT32 P_PointOnDivlineSidePrecise(fixed_t x, fixed_t y, const divline_t *line); void P_MakeDivline(line_t *li, divline_t *dl); void P_CameraLineOpening(line_t *plinedef); fixed_t P_InterceptVector(divline_t *v2, divline_t *v1); diff --git a/src/p_mobj.c b/src/p_mobj.c index 91e091090..c1f8c2abf 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3288,7 +3288,7 @@ void P_MobjCheckWater(mobj_t *mobj) { // Water removes electric and non-water fire shields... if (electric) P_FlashPal(p, PAL_WHITE, 1); - + p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK; } } diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 6431e4624..54ca7beb3 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -27,6 +27,7 @@ #include "r_main.h" #include "r_state.h" #include "r_defs.h" +#include "r_dynseg.h" /* Theory behind Polyobjects: @@ -601,7 +602,7 @@ static void Polyobj_moveToSpawnSpot(mapthing_t *anchor) // Attaches a polyobject to its appropriate subsector. static void Polyobj_attachToSubsec(polyobj_t *po) { - subsector_t *ss; + subsector_t *ss; fixed_t center_x = 0, center_y = 0; fixed_t numVertices; size_t i; @@ -610,6 +611,10 @@ static void Polyobj_attachToSubsec(polyobj_t *po) if (po->isBad) return; + // already attached? + if (po->attached) + return; + numVertices = (fixed_t)(po->numVertices*FRACUNIT); for (i = 0; i < po->numVertices; ++i) @@ -622,25 +627,24 @@ static void Polyobj_attachToSubsec(polyobj_t *po) po->centerPt.y = center_y; ss = R_PointInSubsector(po->centerPt.x, po->centerPt.y); - M_DLListInsert(&po->link, (mdllistitem_t **)(void *)(&ss->polyList)); -#ifdef R_LINKEDPORTALS - // set spawnSpot's groupid for correct portal sound behavior - po->spawnSpot.groupid = ss->sector->groupid; -#endif - - po->attached = true; + R_AttachPolyObject(po); } // Removes a polyobject from the subsector to which it is attached. static void Polyobj_removeFromSubsec(polyobj_t *po) { - if (po->attached) - { - M_DLListRemove(&po->link); - po->attached = false; - } + // a bad polyobject should never have been attached in the first place + if (po->isBad) + return; + + // not attached? + if (!po->attached) + return; + + M_DLListRemove(&po->link); + R_DetachPolyObject(po); } // Blockmap Functions diff --git a/src/p_polyobj.h b/src/p_polyobj.h index 7c814e0bf..56eb0c5a1 100644 --- a/src/p_polyobj.h +++ b/src/p_polyobj.h @@ -76,8 +76,12 @@ typedef struct polyobj_s INT32 parent; // numeric id of parent polyobject - size_t segCount; // number of segs in polyobject - size_t numSegsAlloc; // number of segs allocated + struct subsector_s **dynaSubsecs; // list of subsectors holding fragments + INT32 numDSS; // number of subsector pointers + INT32 numDSSAlloc; // number of subsector pointers allocated + + size_t segCount; // number of segs in polyobject + size_t numSegsAlloc; // number of segs allocated struct seg_s **segs; // the segs, a reallocating array. size_t numVertices; // number of vertices (generally == segCount) @@ -88,16 +92,16 @@ typedef struct polyobj_s size_t numLines; // number of linedefs (generally <= segCount) size_t numLinesAlloc; // number of linedefs allocated - struct line_s **lines; // linedefs this polyobject must move + struct line_s **lines; // linedefs this polyobject must move degenmobj_t spawnSpot; // location of spawn spot vertex_t centerPt; // center point fixed_t zdist; // viewz distance for sorting angle_t angle; // for rotation - UINT8 attached; // if true, is attached to a subsector + UINT8 attached; // if true, is attached to a subsector fixed_t blockbox[4]; // bounding box for clipping - UINT8 linked; // is linked to blockmap + UINT8 linked; // is linked to blockmap size_t validcount; // for clipping: prevents multiple checks INT32 damage; // damage to inflict on stuck things fixed_t thrust; // amount of thrust to put on blocking objects @@ -105,7 +109,7 @@ typedef struct polyobj_s thinker_t *thinker; // pointer to a thinker affecting this polyobj - UINT8 isBad; // a bad polyobject: should not be rendered/manipulated + UINT8 isBad; // a bad polyobject: should not be rendered/manipulated INT32 translucency; // index to translucency tables INT16 triggertag; // Tag of linedef executor to trigger on touch diff --git a/src/p_setup.c b/src/p_setup.c index 51d2f474d..50310fb92 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -33,6 +33,7 @@ #include "r_picformats.h" #include "r_sky.h" #include "r_draw.h" +#include "r_dynseg.h" #include "s_sound.h" #include "st_stuff.h" @@ -103,6 +104,7 @@ seg_t *segs; sector_t *sectors; subsector_t *subsectors; node_t *nodes; +fnode_t *fnodes; line_t *lines; side_t *sides; mapthing_t *mapthings; @@ -948,6 +950,12 @@ void P_WriteThings(void) // MAP LOADING FUNCTIONS // +static void P_InitializeVertex(vertex_t *vt) +{ + vt->floorzset = vt->ceilingzset = false; + vt->floorz = vt->ceilingz = 0; +} + static void P_LoadVertices(UINT8 *data) { mapvertex_t *mv = (mapvertex_t *)data; @@ -959,8 +967,7 @@ static void P_LoadVertices(UINT8 *data) { v->x = SHORT(mv->x)<y = SHORT(mv->y)<floorzset = v->ceilingzset = false; - v->floorz = v->ceilingz = 0; + P_InitializeVertex(v); } } @@ -1136,6 +1143,23 @@ static void P_SetLinedefV2(size_t i, UINT16 vertex_num) lines[i].v2 = &vertexes[vertex_num]; } +// +// P_MakeLineNormal +// +// Calculates a 2D normal for the given line and stores it in the line +// +static void P_MakeLineNormal(line_t *line) +{ + float linedx, linedy, length; + + linedx = FixedToFloat(line->v2->x) - FixedToFloat(line->v1->x); + linedy = FixedToFloat(line->v2->y) - FixedToFloat(line->v1->y); + + length = (float)sqrt(linedx * linedx + linedy * linedy); + line->nx = linedy / length; + line->ny = -linedx / length; +} + static void P_LoadLinedefs(UINT8 *data) { maplinedef_t *mld = (maplinedef_t *)data; @@ -1158,6 +1182,7 @@ static void P_LoadLinedefs(UINT8 *data) ld->sidenum[1] = SHORT(mld->sidenum[1]); P_InitializeLinedef(ld); + P_MakeLineNormal(ld); } } @@ -1828,8 +1853,7 @@ static void P_LoadTextmap(void) { // Defaults. vt->x = vt->y = INT32_MAX; - vt->floorzset = vt->ceilingzset = false; - vt->floorz = vt->ceilingz = 0; + P_InitializeVertex(vt); TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter); @@ -1905,6 +1929,7 @@ static void P_LoadTextmap(void) I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i)); P_InitializeLinedef(ld); + P_MakeLineNormal(ld); } for (i = 0, sd = sides; i < numsides; i++, sd++) @@ -2121,6 +2146,29 @@ static inline void P_LoadSubsectors(UINT8 *data) } } +// +// P_CalcNodeCoefficients +// +// haleyjd 06/14/10: Separated from P_LoadNodes, this routine precalculates +// general line equation coefficients for a node, which are used during the +// process of dynaseg generation. +// +static void P_CalcNodeCoefficients(node_t *node, fnode_t *fnode) +{ + // haleyjd 05/16/08: keep floating point versions as well for dynamic + // seg splitting operations + double fx = (double)FixedToFloat(node->x); + double fy = (double)FixedToFloat(node->y); + double fdx = (double)FixedToFloat(node->dx); + double fdy = (double)FixedToFloat(node->dy); + + // haleyjd 05/20/08: precalculate general line equation coefficients + fnode->a = -fdy; + fnode->b = fdx; + fnode->c = fdy * fx - fdx * fy; + fnode->len = sqrt(fdx * fdx + fdy * fdy); +} + static void P_LoadNodes(UINT8 *data) { UINT8 j, k; @@ -2134,6 +2182,10 @@ static void P_LoadNodes(UINT8 *data) no->y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = SHORT(mn->children[j]); @@ -2148,7 +2200,7 @@ static void P_LoadNodes(UINT8 *data) * \param seg Seg to compute length for. * \return Length in fracunits. */ -static fixed_t P_SegLength(seg_t *seg) +fixed_t P_SegLength(seg_t *seg) { INT64 dx = (seg->v2->x - seg->v1->x)>>1; INT64 dy = (seg->v2->y - seg->v1->y)>>1; @@ -2446,6 +2498,7 @@ static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype) numnodes = READINT32((*data)); nodes = Z_Calloc(numnodes*sizeof(*nodes), PU_LEVEL, NULL); + fnodes = Z_Calloc(numnodes*sizeof(*fnodes), PU_LEVEL, NULL); for (i = 0, mn = nodes; i < numnodes; i++, mn++) { @@ -2455,6 +2508,9 @@ static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype) mn->dx = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS); mn->dy = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS); + // haleyjd: calculate floating-point data + P_CalcNodeCoefficients(mn, &fnodes[i]); + // Bounding boxes for (j = 0; j < 2; j++) for (k = 0; k < 4; k++) @@ -2492,6 +2548,7 @@ static void P_LoadMapBSP(const virtres_t *virt) subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL); nodes = Z_Calloc(numnodes * sizeof(*nodes), PU_LEVEL, NULL); + fnodes = Z_Calloc(numnodes * sizeof(*fnodes), PU_LEVEL, NULL); segs = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL); P_LoadSubsectors(virtssectors->data); @@ -3235,7 +3292,7 @@ static void P_ConvertBinaryMap(void) lines[i].args[4] |= TMSC_BACKTOFRONTCEILING; lines[i].special = 720; break; - + case 900: //Translucent wall (10%) case 901: //Translucent wall (20%) case 902: //Translucent wall (30%) @@ -4228,6 +4285,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) Z_Free(ss->attachedsolid); } + // haleyjd 05/16/08: clear dynamic segs + R_ClearDynaSegs(); + // Clear pointers that would be left dangling by the purge R_FlushTranslationColormapCache(); diff --git a/src/p_setup.h b/src/p_setup.h index 9fa70d516..0d60c99eb 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -93,6 +93,8 @@ INT32 P_CheckLevelFlat(const char *flatname); extern size_t nummapthings; extern mapthing_t *mapthings; +fixed_t P_SegLength(seg_t *seg); + void P_SetupLevelSky(INT32 skynum, boolean global); #ifdef SCANTHINGS void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum); diff --git a/src/p_spec.c b/src/p_spec.c index 742fd544b..d6f75046f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -5188,13 +5188,13 @@ static void P_PlayerOnSpecial3DFloor(player_t *player, sector_t *sector) continue; } - // We're inside it! Yess... if (!polysec->special) { po = (polyobj_t *)(po->link.next); continue; } + // We're inside it! Yess... if (!(po->flags & POF_TESTHEIGHT)) // Don't do height checking ; else if (po->flags & POF_SOLID) diff --git a/src/r_bsp.c b/src/r_bsp.c index 5acd4e70c..f030b46e2 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -16,6 +16,8 @@ #include "r_local.h" #include "r_state.h" #include "r_portal.h" // Add seg portals +#include "r_dynabsp.h" +#include "r_dynseg.h" #include "r_splats.h" #include "p_local.h" // camera @@ -716,106 +718,53 @@ void R_SortPolyObjects(subsector_t *sub) } // -// R_PolysegCompare +// Recurse through a polynode mini-BSP // -// 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) +static void R_renderPolyNode(const rpolynode_t *node) { - const seg_t *seg1 = *(const seg_t * const *)p1; - const seg_t *seg2 = *(const seg_t * const *)p2; - fixed_t dist1v1, dist1v2, dist2v1, dist2v2; + while(node) + { + seg_t *seg = &node->partition->seg; - // 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) + // render frontspace + int side = R_PointOnDynaSegSide(node->partition, FixedToFloat(viewx), FixedToFloat(viewy)); + R_renderPolyNode(node->children[side]); - dist1v1 = vxdist(seg1->v1); - dist1v2 = vxdist(seg1->v2); - dist2v1 = vxdist(seg2->v1); - dist2v2 = vxdist(seg2->v2); + // render partition seg + seg->angle = R_PointToAngleEx(seg->v1->x, seg->v1->y, seg->v2->x, seg->v2->y); + seg->polyseg = node->partition->polyobj; + R_AddLine(seg); - if (min(dist1v1, dist1v2) != min(dist2v1, dist2v2)) - return min(dist1v1, dist1v2) - 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); + // continue to render backspace + node = node->children[side^1]; } -#undef vxdist -#undef pdist } // -// R_AddPolyObjects +// haleyjd: Adds dynamic segs contained in all of the rpolyobj_t fragments +// contained inside the given subsector into a mini-BSP tree and then +// renders the BSP. BSPs are only recomputed when polyobject fragments +// move into or out of the subsector. This is the ultimate heart of the +// polyobject code. // -// haleyjd 02/19/06 -// Adds all segs in all polyobjects in the given subsector. +// See r_dynseg.c to see how dynasegs get attached to a subsector in the +// first place :) // -static void R_AddPolyObjects(subsector_t *sub) +// See r_dynabsp.c for rpolybsp generation. +// +static void R_addDynaSegs(subsector_t *sub) { - polyobj_t *po = sub->polyList; - size_t i, j; + boolean needbsp = (!sub->bsp || sub->bsp->dirty); - numpolys = 0; - - // count polyobjects - while (po) + if(needbsp) { - ++numpolys; - po = (polyobj_t *)(po->link.next); + if(sub->bsp) + R_FreeDynaBSP(sub->bsp); + sub->bsp = R_BuildDynaBSP(sub); } - // for render stats - ps_numpolyobjects += numpolys; - - // sort polyobjects - R_SortPolyObjects(sub); - - // render polyobjects - for (i = 0; i < numpolys; ++i) - { - qsort(po_ptrs[i]->segs, po_ptrs[i]->segCount, sizeof(seg_t *), R_PolysegCompare); - for (j = 0; j < po_ptrs[i]->segCount; ++j) - R_AddLine(po_ptrs[i]->segs[j]); - } + if(sub->bsp) + R_renderPolyNode(sub->bsp->root); } // @@ -988,19 +937,21 @@ static void R_Subsector(size_t num) } // Polyobjects have planes, too! - if (sub->polyList) + if (sub->renderPolyList) { - polyobj_t *po = sub->polyList; + rpolyobj_t *rpo = sub->renderPolyList; sector_t *polysec; - while (po) + while (rpo) { + polyobj_t *po = rpo->polyobj; + if (numffloors >= MAXFFLOORS) break; if (!(po->flags & POF_RENDERPLANES)) // Don't draw planes { - po = (polyobj_t *)(po->link.next); + rpo = (rpolyobj_t *)(rpo->link.next); continue; } @@ -1049,7 +1000,7 @@ static void R_Subsector(size_t num) numffloors++; } - po = (polyobj_t *)(po->link.next); + rpo = (rpolyobj_t *)(rpo->link.next); } } @@ -1070,8 +1021,8 @@ static void R_Subsector(size_t num) firstseg = NULL; // haleyjd 02/19/06: draw polyobjects before static lines - if (sub->polyList) - R_AddPolyObjects(sub); + if (sub->renderPolyList) + R_addDynaSegs(sub); while (count--) { diff --git a/src/r_defs.h b/src/r_defs.h index 1be3a1b8c..4b4587c65 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -24,10 +24,6 @@ #include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT -#ifdef HWRENDER -#include "m_aatree.h" -#endif - #include "taglist.h" // @@ -386,6 +382,7 @@ typedef struct line_s vertex_t *v2; fixed_t dx, dy; // Precalculated v2 - v1 for side checking. + float nx, ny; // SoM 05/11/09: Pre-calculated 2D normal for the line // Animation related. INT16 flags; @@ -453,7 +450,9 @@ typedef struct subsector_s sector_t *sector; INT16 numlines; UINT16 firstline; - struct polyobj_s *polyList; // haleyjd 02/19/06: list of polyobjects + struct polyobj_s *polyList; // List of polyobjects that physically exist in this subsector. + struct rpolyobj_s *renderPolyList; // haleyjd 02/19/06: list of polyobjects + struct rpolybsp_s *bsp; // haleyjd 05/05/13: sub-BSP tree size_t validcount; } subsector_t; @@ -523,8 +522,11 @@ typedef struct lightmap_s // typedef struct seg_s { - vertex_t *v1; - vertex_t *v2; + union + { + struct { vertex_t *v1, *v2; }; + struct { struct dynavertex_s *dyv1, *dyv2; }; + }; INT32 side; @@ -541,6 +543,7 @@ typedef struct seg_s sector_t *backsector; fixed_t length; // precalculated seg length + #ifdef HWRENDER // new pointers so that AdjustSegs doesn't mess with v1/v2 void *pv1; // polyvertex_t @@ -550,10 +553,13 @@ typedef struct seg_s lightmap_t *lightmaps; // for static lightmap #endif + polyobj_t *polyseg; + sector_t *polysector; + // Why slow things down by calculating lightlists for every thick side? size_t numlights; r_lightlist_t *rlights; - polyobj_t *polyseg; + boolean dontrenderme; boolean glseg; } seg_t; @@ -574,6 +580,20 @@ typedef struct UINT16 children[2]; } node_t; +// +// fnode +// +// haleyjd 12/07/12: The fnode structure holds floating-point general line +// equation coefficients and float versions of partition line coordinates and +// lengths. It is kept separate from node_t for purposes of not causing that +// structure to become cache inefficient. +// +typedef struct fnode_s +{ + double a, b, c; // haleyjd 05/20/08: coefficients for general line equation + double len; // length of partition line, for normalization +} fnode_t; + #if defined(_MSC_VER) #pragma pack(1) #endif diff --git a/src/r_dynabsp.c b/src/r_dynabsp.c new file mode 100644 index 000000000..23ccf7674 --- /dev/null +++ b/src/r_dynabsp.c @@ -0,0 +1,719 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2013 James Haley et al. +// +// Portions derived from BSP 5.2, "Node builder for DOOM levels" +// (c) 1996 Raphael Quinet +// (c) 1998 Colin Reed, Lee Killough +// (c) 2001 Simon Howard +// (c) 2006 Colin Phipps +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Dynamic BSP sub-trees for dynaseg sorting. +// +//----------------------------------------------------------------------------- + +#include "z_zone.h" + +#include "p_setup.h" +#include "r_dynabsp.h" + +//============================================================================= +// +// rpolynode Maintenance +// + +static rpolynode_t *polyNodeFreeList; + +// +// R_GetFreePolyNode +// +// Gets a node from the free list or allocates a new one. +// +static rpolynode_t *R_GetFreePolyNode(void) +{ + rpolynode_t *ret = NULL; + + if(polyNodeFreeList) + { + ret = polyNodeFreeList; + polyNodeFreeList = polyNodeFreeList->children[0]; + memset(ret, 0, sizeof(*ret)); + } + else + ret = calloc(sizeof(rpolynode_t), 1); + + return ret; +} + +// +// R_FreePolyNode +// +// Puts a dynamic node onto the free list. +// +static void R_FreePolyNode(rpolynode_t *rpn) +{ + rpn->children[0] = polyNodeFreeList; + polyNodeFreeList = rpn; +} + +//============================================================================= +// +// Dynaseg Setup +// + +static void InsertIntoNodeList(dsegnode_t **tail, dynaseg_t *seg) +{ + dsegnode_t *node = Z_Calloc(sizeof(dsegnode_t), PU_STATIC, NULL); + node->dynaseg = seg; + + if (*tail) + { + node->prev = (*tail); + (*tail)->next = node; + } + + (*tail) = node; +} + +static void RemoveFromNodeList(dsegnode_t *node, dsegnode_t **tail, dsegnode_t **head) +{ + if (node->next) + node->next->prev = node->prev; + else + (*tail) = node->prev; + + if (node->prev) + node->prev->next = node->next; + else + (*head) = node->next; + + Z_Free(node); +} + +static void FindNodeListHead(dsegnode_t **list) +{ + if (!*list) + return; + while ((*list)->prev) + (*list) = (*list)->prev; +} + +static void FindNodeListTail(dsegnode_t **list) +{ + if (!*list) + return; + while ((*list)->next) + (*list) = (*list)->next; +} + +// +// R_setupDSForBSP +// +// Dynasegs need a small amount of preparation in order to achieve maximum +// efficiency during the node building process. +// +static void R_setupDSForBSP(dynaseg_t *ds) +{ + // Fast access to double-precision coordinates + ds->psx = FixedToFloat(ds->seg.v1->x); + ds->psy = FixedToFloat(ds->seg.v1->y); + ds->pex = FixedToFloat(ds->seg.v2->x); + ds->pey = FixedToFloat(ds->seg.v2->y); + + // Fast access to delta x, delta y + ds->pdx = ds->pex - ds->psx; + ds->pdy = ds->pey - ds->psy; + + // General line equation coefficient 'c' + ds->ptmp = ds->pdx * ds->psy - ds->psx * ds->pdy; + + // Length - we DEFINITELY don't want to do this any more than necessary + ds->len = sqrt(ds->pdx * ds->pdx + ds->pdy * ds->pdy); +} + +//============================================================================= +// +// Partition Selection +// + +// I am not TOO worried about accuracy during partition selection, but... +#define CHECK_EPSILON 0.0001 + +static inline boolean nearzero(double d) +{ + return (d < CHECK_EPSILON && d > -CHECK_EPSILON); +} + +// split weighting factor +// I have no idea why "17" is good, but let's stick with it. +#define FACTOR 17 + +// +// R_selectPartition +// +// This routine decides the best dynaseg to use as a nodeline by attempting to +// minimize the number of splits it would cause in remaining dynasegs. +// +// Credit to Raphael Quinet and DEU. +// +// Rewritten by Lee Killough for significant performance increases. +// (haleyjd - using gotos, naturally ;) +// +static dsegnode_t *R_selectPartition(dsegnode_t *segs) +{ + dsegnode_t *rover; + dsegnode_t *best = NULL; + int bestcost = INT_MAX; + int cnt = 0; + + // count the number of segs on the input list + for(rover = segs; rover; rover = rover->next) + ++cnt; + + // Try each seg as a partition line + for(rover = segs; rover; rover = rover->next) + { + dynaseg_t *part = rover->dynaseg; + dsegnode_t *crover; + int cost = 0, tot = 0, diff = cnt; + + // haleyjd: add one seg worth of cost to non-orthogonal lines + if(part->seg.linedef->slopetype > ST_VERTICAL) + cost += FACTOR; + + // Check partition against all segs + for(crover = segs; crover; crover = crover->next) + { + dynaseg_t *check = crover->dynaseg; + + // classify both end points + double a = part->pdy * check->psx - part->pdx * check->psy + part->ptmp; + double b = part->pdy * check->pex - part->pdx * check->pey + part->ptmp; + + if(!(a*b >= 0)) // oppositely signed? + { + if(!nearzero(a) && !nearzero(b)) // not within epsilon of 0? + { + // line is split + double l = check->len; + double d = (l * a) / (a - b); // distance from intersection + + if(d >= 2.0) + { + cost += FACTOR; + + // killough: This is the heart of my pruning idea - it + // catches bad segs early on. + if(cost > bestcost) + goto prune; + + ++tot; + } + else if(l - d < 2.0 ? check->pdx * part->pdx + check->pdy * part->pdy < 0 : b < 0) + goto leftside; + } + else + goto leftside; + } + else if(a <= 0 && (!nearzero(a) || + (nearzero(b) && check->pdx * part->pdx + check->pdy * part->pdy < 0))) + { +leftside: + diff -= 2; + } + } // end for + + // Take absolute value; diff is being used to obtain the min/max values + // by way of min(a, b) = (a + b - abs(a - b)) / 2 + if((diff -= tot) < 0) + diff = -diff; + + // Make sure at least one seg is on each side of the partition + if(tot + cnt > diff && (cost += diff) < bestcost) + { + // we have a new better choice + bestcost = cost; + best = rover; // remember the best seg + } +prune: ; // early exit and skip past the tests above + } // end for + + // haleyjd: failsafe. Maybe there's just one left in the list. I'm not + // taking any chances that the above algorithm might freak out when that + // becomes the case. I KNOW the list is not empty. + if(!best) + best = segs; + + return best; // All finished, return best seg +} + +//============================================================================= +// +// Tree Building +// + +// +// Calculate the point of intersection of two lines +// +void R_ComputeIntersection(const dynaseg_t *part, const dynaseg_t *seg, double *outx, double *outy) +{ + double a2, b2, l2, w, d; + double dx, dy, dx2, dy2; + + dx = part->pex - part->psx; + dy = part->pey - part->psy; + dx2 = seg->pex - seg->psx; + dy2 = seg->pey - seg->psy; + + l2 = sqrt(dx2*dx2 + dy2*dy2); + + if(l2 == 0.0) + { + // feh. + *outx = seg->psx; + *outy = seg->psy; + return; + } + + a2 = dx2 / l2; + b2 = dy2 / l2; + d = dy * a2 - dx * b2; + + if(d != 0.0) + { + w = (dx * (seg->psy - part->psy) + dy * (part->psx - seg->psx)) / d; + (*outx) = seg->psx + (a2 * w); + (*outy) = seg->psy + (b2 * w); + } + else + { + (*outx) = seg->psx; + (*outy) = seg->psy; + } +} + +// return-type enumeration for R_classifyDynaSeg +enum +{ + END_ON = 0x01, + END_LEFT = 0x02, + END_RIGHT = 0x04, + + START_ON = 0x10, + START_LEFT = 0x20, + START_RIGHT = 0x40, + + // cases where seg must be split + SPLIT_SL_ER = (START_LEFT | END_RIGHT), + SPLIT_SR_EL = (START_RIGHT | END_LEFT ), + + // cases where seg is not split + CLASSIFY_LEFT = (START_LEFT | END_LEFT ), + CLASSIFY_RIGHT = (START_RIGHT | END_RIGHT), + CLASSIFY_ON = (START_ON | END_ON ) +}; + +// +// R_classifyDynaSeg +// +// The seg is either left, right, or on the partition line. +// +static int R_classifyDynaSeg(const dynaseg_t *part, const dynaseg_t *seg, double pdx, double pdy) +{ + double dx2, dy2, dx3, dy3, a, b; + + // check line against partition + dx2 = part->psx - seg->psx; + dy2 = part->psy - seg->psy; + dx3 = part->psx - seg->pex; + dy3 = part->psy - seg->pey; + + a = pdy * dx2 - pdx * dy2; + b = pdy * dx3 - pdx * dy3; + + if(!(a*b >= 0) && !nearzero(a) && !nearzero(b)) + { + double x = 0.0, y = 0.0; + + // line is split + R_ComputeIntersection(part, seg, &x, &y); + + // find distance from line start to split point + dx2 = seg->psx - x; + dy2 = seg->psy - y; + + if(nearzero(dx2) && nearzero(dy2)) + a = 0.0; + else + { + double l = dx2*dx2 + dy2*dy2; + if(l < 4.0) + a = 0.0; + } + + dx3 = seg->pex - x; + dy3 = seg->pey - y; + + if(nearzero(dx3) && nearzero(dy3)) + b = 0.0; + else + { + double l = dx3*dx3 + dy3*dy3; + if(l < 4.0) + b = 0.0; + } + } + + int val = 0; + + if(nearzero(a)) // start is on the partition + val |= START_ON; + else if(a < 0.0) // start is on left + val |= START_LEFT; + else // start is on right + val |= START_RIGHT; + + if(nearzero(b)) // end is on partition + val |= END_ON; + else if(b < 0.0) // end is on left + val |= END_LEFT; + else // end is on right + val |= END_RIGHT; + + return val; +} + + +// +// R_divideSegs +// +// Split the input list of segs into left and right lists using one of the segs +// selected as a partition line for the current node. +// +static void R_divideSegs(rpolynode_t *rpn, dsegnode_t **ts, dsegnode_t **rs, dsegnode_t **ls) +{ + dynaseg_t *best, *add_to_rs = NULL, *add_to_ls = NULL; + dsegnode_t **tail = ts, **head = ts, *partition; + + double pdx, pdy; + dsegnode_t *cur; + + // select best seg to use as partition line + partition = R_selectPartition(*ts); + best = rpn->partition = partition->dynaseg; + + head = tail = ts; + FindNodeListTail(tail); + RemoveFromNodeList(partition, head, tail); + +#ifdef RANGECHECK + // Should not happen. + if(!rpn->partition) + I_Error("R_divideSegs: could not select partition!\n"); +#endif + + // use the partition line to split any other lines in the list that intersect + // it into left and right halves + pdx = best->psx - best->pex; + pdy = best->psy - best->pey; + + // iterate from beginning until the original list is empty + while ((cur = *head)) + { + dynaseg_t *seg = cur->dynaseg; + add_to_ls = add_to_rs = NULL; + + int val = R_classifyDynaSeg(best, seg, pdx, pdy); + + if(val == SPLIT_SR_EL || val == SPLIT_SL_ER) + { + double x, y; + + // seg is split by the partition + R_ComputeIntersection(best, seg, &x, &y); + + // create a new vertex at the intersection point + dynavertex_t *nv = R_GetFreeDynaVertex(); + nv->x = FloatToFixed(x); + nv->y = FloatToFixed(y); + + // create a new dynaseg from nv to v2 + dynaseg_t *nds = R_CreateDynaSeg(seg, nv, seg->seg.dyv2); + R_setupDSForBSP(nds); + nds->seg.polysector = seg->seg.polysector; + nds->seg.frontsector = seg->seg.frontsector; + nds->seg.backsector = seg->seg.backsector; + nds->seg.length = FloatToFixed(nds->len); + + // modify original seg to run from v1 to nv + boolean notmarked = !seg->originalv2; + if(notmarked) // only if not already marked! + R_SetDynaVertexRef(&seg->originalv2, seg->seg.dyv2); + R_SetDynaVertexRef(&seg->seg.dyv2, nv); + R_setupDSForBSP(seg); + seg->seg.length = FloatToFixed(seg->len); + + // add the new seg to the current node's ownership list, + // so it can get freed later + M_DLListInsert(&nds->ownerlink.link, (mdllistitem_t **)(&rpn->owned)); + nds->ownerlink.link.next = NULL; + nds->ownerlink.dynaseg = nds; + + if(notmarked) + { + M_DLListInsert(&seg->alterlink.link, (mdllistitem_t **)(&rpn->altered)); + seg->alterlink.link.next = NULL; + seg->alterlink.dynaseg = seg; + } + + // classify left or right + if(val == SPLIT_SR_EL) + { + add_to_ls = nds; + add_to_rs = seg; + } + else + { + add_to_ls = seg; + add_to_rs = nds; + } + } + else + { + // Not split; which side? + if(val & CLASSIFY_LEFT) + add_to_ls = seg; // at least one vertex is left, other is left or on + if(val & CLASSIFY_RIGHT) + add_to_rs = seg; // at least one vertex is right, other is right or on + if(val == CLASSIFY_ON) + { + // We know the segs are parallel or nearly so; take their dot + // product to determine their relative orientation + if((seg->psx - seg->pex) * pdx + (seg->psy - seg->pey) * pdy < 0.0) + add_to_ls = seg; + else + add_to_rs = seg; + } + } + + // add to right side? + if(add_to_rs) + { + //CONS_Printf("Added %p into right side\n", cur); + if (add_to_rs == seg) + RemoveFromNodeList(cur, head, tail); + InsertIntoNodeList(rs, add_to_rs); + } + + // add to left side? + if(add_to_ls) + { + //CONS_Printf("Added %p into left side\n", cur); + if (add_to_ls == seg) + RemoveFromNodeList(cur, head, tail); + InsertIntoNodeList(ls, add_to_ls); + } + } + + FindNodeListHead(rs); + FindNodeListHead(ls); +} + +// +// R_createNode +// +// Primary recursive node building routine. Partition the segs using one of the +// segs as the partition line, then recurse into the back and front spaces until +// there are no segs left to classify. +// +// A tree of rpolynode instances is returned. NULL is returned in the terminal +// case where there are no segs left to classify. +// +static rpolynode_t *R_createNode(dsegnode_t **ts) +{ + dsegnode_t *rights = NULL; + dsegnode_t *lefts = NULL; + + if(!*ts) + return NULL; // terminal case: empty list + + rpolynode_t *rpn = R_GetFreePolyNode(); + + // divide the segs into two lists + R_divideSegs(rpn, ts, &rights, &lefts); + + // recurse into right space + rpn->children[0] = R_createNode(&rights); + + // recurse into left space + rpn->children[1] = R_createNode(&lefts); + + return rpn; +} + +//============================================================================= +// +// Transform subsector fragments +// + +// +// R_collapseFragmentsToDSList +// +// Take a subsector and turn its list of rpolyobj fragments into a flat list of +// dynasegs linked by their bsplinks. The dynasegs' BSP-related fields will also +// be initialized. The result is suitable for input to R_BuildDynaBSP. +// +static boolean R_collapseFragmentsToDSList(const subsector_t *subsec, dsegnode_t **list) +{ + rpolyobj_t *fragment = subsec->renderPolyList; + + // Nothing to do? (We shouldn't really be called in that case, but, hey...) + if(!fragment) + return false; + + while(fragment) + { + dynaseg_t *ds = fragment->dynaSegs; + + while(ds) + { + R_setupDSForBSP(ds); + + InsertIntoNodeList(list, ds); + + // NB: fragment links are not disturbed by this process. + ds = ds->subnext; + } + + fragment = (rpolyobj_t *)(fragment->link.next); + } + + FindNodeListHead(list); + + return (*list != NULL); +} + +//============================================================================= +// +// Freeing +// +// Almost as much work as building it was :P +// + +// +// R_returnOwnedList +// +// Return all dynasegs on a node's "owned" list. These are dynasegs that were +// created during the splitting process and are not referenced by rpolyobj_t +// instances. +// +static void R_returnOwnedList(rpolynode_t *node) +{ + dseglink_t *dsl = node->owned; + + while(dsl) + { + dseglink_t *next = (dseglink_t *)(dsl->link.next); + dynaseg_t *ds = dsl->dynaseg; + + R_FreeDynaSeg(ds); + + dsl = next; + } + + // Now also fix altered polyobject dynasegs + dsl = node->altered; + while(dsl) + { + dseglink_t *next = (dseglink_t *)(dsl->link.next); + dynaseg_t *ds = dsl->dynaseg; + R_SetDynaVertexRef(&ds->seg.dyv2, ds->originalv2); + R_FreeDynaVertex(&ds->originalv2); + P_CalcDynaSegLength(ds); + M_DLListRemove(&dsl->link); + dsl = next; + } +} + +// +// R_freeTreeRecursive +// +// Recursively free the BSP tree nodes. +// +static void R_freeTreeRecursive(rpolynode_t *root) +{ + if(!root) + return; + + // free right and left sides + R_freeTreeRecursive(root->children[0]); + R_freeTreeRecursive(root->children[1]); + + // free resources stored in this node + R_returnOwnedList(root); + + // return the bsp node + R_FreePolyNode(root); +} + +//============================================================================= +// +// External Interface +// + +// +// R_BuildDynaBSP +// +// Call to build a dynamic BSP sub-tree for sorting of dynasegs. +// +rpolybsp_t *R_BuildDynaBSP(const subsector_t *subsec) +{ + rpolybsp_t *bsp = NULL; + dsegnode_t *segs = NULL; + + if(R_collapseFragmentsToDSList(subsec, &segs)) + { + bsp = Z_Calloc(sizeof(rpolybsp_t), PU_LEVEL, NULL); + bsp->dirty = false; + bsp->root = R_createNode(&segs); + } + + while (segs) + { + dsegnode_t *next = segs->next; + Z_Free(segs); + segs = next; + } + + return bsp; +} + +// +// R_FreeDynaBSP +// +// Return all resources owned by a dynamic BSP tree. +// +void R_FreeDynaBSP(rpolybsp_t *bsp) +{ + R_freeTreeRecursive(bsp->root); + Z_Free(bsp); +} + +// EOF diff --git a/src/r_dynabsp.h b/src/r_dynabsp.h new file mode 100644 index 000000000..c4b615675 --- /dev/null +++ b/src/r_dynabsp.h @@ -0,0 +1,62 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2013 James Haley et al. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Dynamic BSP sub-trees for dynaseg sorting. +// +//----------------------------------------------------------------------------- + +#ifndef R_DYNABSP_H__ +#define R_DYNABSP_H__ + +#include "r_dynseg.h" + +typedef struct rpolynode_s +{ + dynaseg_t *partition; // partition dynaseg + struct rpolynode_s *children[2]; // child node lists (0=right, 1=left) + dseglink_t *owned; // owned segs created by partition splits + dseglink_t *altered; // polyobject-owned segs altered by partitions. +} rpolynode_t; + +typedef struct rpolybsp_s +{ + boolean dirty; // needs to be rebuilt if true + rpolynode_t *root; // root of tree +} rpolybsp_t; + +rpolybsp_t *R_BuildDynaBSP(const subsector_t *subsec); +void R_FreeDynaBSP(rpolybsp_t *bsp); + +// +// R_PointOnDynaSegSide +// +// Returns 0 for front/right, 1 for back/left. +// +static inline int R_PointOnDynaSegSide(const dynaseg_t *ds, float x, float y) +{ + return ((ds->pdx * (y - ds->psy)) >= (ds->pdy * (x - ds->psx))); +} + +void R_ComputeIntersection(const dynaseg_t *part, const dynaseg_t *seg, double *outx, double *outy); + +#endif + +// EOF diff --git a/src/r_dynseg.c b/src/r_dynseg.c new file mode 100644 index 000000000..a4f5cc460 --- /dev/null +++ b/src/r_dynseg.c @@ -0,0 +1,761 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2013 James Haley et al. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +// Additional terms and conditions compatible with the GPLv3 apply. See the +// file COPYING-EE for details. +// +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Dynamic segs for PolyObject re-implementation. +// +//----------------------------------------------------------------------------- + +#include "z_zone.h" +#include "i_system.h" +#include "m_bbox.h" +#include "p_maputl.h" +#include "r_main.h" +#include "r_dynseg.h" +#include "r_dynabsp.h" +#include "r_state.h" + +// +// dynaseg free list +// +// Let's do as little allocation as possible. +// +static dynaseg_t *dynaSegFreeList; + +// +// dynaseg vertex free list +// +static dynavertex_t *dynaVertexFreeList; + +// +// rpolyobj_t freelist +// +static rpolyobj_t *freePolyFragments; + +// +// Used for dynasegs, not base segs +// +void P_CalcDynaSegLength(dynaseg_t *dynaseg) +{ + seg_t *lseg = &dynaseg->seg; + lseg->length = P_SegLength(lseg); +#ifdef HWRENDER + lseg->flength = FixedToFloat(lseg->length); +#endif +} + +// +// R_AddDynaSubsec +// +// Keeps track of pointers to subsectors which hold dynasegs in a +// reallocating array, for purposes of later detaching the dynasegs. +// Each polyobject contains its own subsector array. +// +static void R_AddDynaSubsec(subsector_t *ss, polyobj_t *po) +{ + int i; + + // If the subsector has a BSP tree, it will need to be rebuilt. + if(ss->bsp) + ss->bsp->dirty = true; + + // make sure subsector is not already tracked + for(i = 0; i < po->numDSS; ++i) + { + if(po->dynaSubsecs[i] == ss) + return; + } + + if(po->numDSS >= po->numDSSAlloc) + { + po->numDSSAlloc = po->numDSSAlloc ? po->numDSSAlloc * 2 : 8; + po->dynaSubsecs = Z_Realloc(po->dynaSubsecs, po->numDSSAlloc * sizeof(subsector_t *), PU_LEVEL, NULL); + } + + po->dynaSubsecs[po->numDSS++] = ss; +} + +// +// R_GetFreeDynaVertex +// +// Gets a vertex from the free list or allocates a new one. +// +dynavertex_t *R_GetFreeDynaVertex(void) +{ + dynavertex_t *ret = NULL; + + if(dynaVertexFreeList) + { + ret = dynaVertexFreeList; + dynaVertexFreeList = dynaVertexFreeList->dynanext; + memset(ret, 0, sizeof(dynavertex_t)); + } + else + ret = calloc(sizeof(dynavertex_t), 1); + + return ret; +} + +// +// R_FreeDynaVertex +// +// Puts a dynamic vertex onto the free list, if its refcount becomes zero. +// +void R_FreeDynaVertex(dynavertex_t **vtx) +{ + dynavertex_t *v; + + if(!*vtx) + return; + + v = *vtx; + + if(v->refcount > 0) + { + v->refcount--; + if(v->refcount == 0) + { + v->refcount = -1; + v->dynanext = dynaVertexFreeList; + dynaVertexFreeList = v; + } + } + + *vtx = NULL; +} + +// +// R_SetDynaVertexRef +// +// Safely set a reference to a dynamic vertex, maintaining the reference count. +// Do not assign dynavertex pointers without using this routine! Note that if +// *target already points to a vertex, that vertex WILL be freed if its ref +// count reaches zero. +// +void R_SetDynaVertexRef(dynavertex_t **target, dynavertex_t *vtx) +{ + if(*target) + R_FreeDynaVertex(target); + + if((*target = vtx)) + (*target)->refcount++; +} + +// +// R_GetFreeDynaSeg +// +// Gets a dynaseg from the free list or allocates a new one. +// +static dynaseg_t *R_GetFreeDynaSeg(void) +{ + dynaseg_t *ret = NULL; + + if(dynaSegFreeList) + { + ret = dynaSegFreeList; + dynaSegFreeList = dynaSegFreeList->freenext; + memset(ret, 0, sizeof(dynaseg_t)); + } + else + ret = calloc(sizeof(dynaseg_t), 1); + + return ret; +} + +// +// R_FreeDynaSeg +// +// Puts a dynaseg onto the free list. +// +void R_FreeDynaSeg(dynaseg_t *dseg) +{ + R_FreeDynaVertex(&dseg->seg.dyv1); + R_FreeDynaVertex(&dseg->seg.dyv2); + R_FreeDynaVertex(&dseg->originalv2); + + M_DLListRemove(&dseg->alterlink.link); // remove it from alterable list + dseg->freenext = dynaSegFreeList; + dynaSegFreeList = dseg; +} + +// +// R_GetFreeRPolyObj +// +// Gets an rpolyobj_t from the free list or creates a new one. +// +static rpolyobj_t *R_GetFreeRPolyObj(void) +{ + rpolyobj_t *ret = NULL; + + if(freePolyFragments) + { + ret = freePolyFragments; + freePolyFragments = freePolyFragments->freenext; + memset(ret, 0, sizeof(rpolyobj_t)); + } + else + ret = calloc(sizeof(rpolyobj_t), 1); + + return ret; +} + +// +// R_FreeRPolyObj +// +// Puts an rpolyobj_t on the freelist. +// +static void R_FreeRPolyObj(rpolyobj_t *rpo) +{ + rpo->freenext = freePolyFragments; + freePolyFragments = rpo; +} + +// +// R_FindFragment +// +// Looks in the given subsector for a polyobject fragment corresponding +// to the given polyobject. If one is not found, then a new one is created +// and returned. +// +static rpolyobj_t *R_FindFragment(subsector_t *ss, polyobj_t *po) +{ + rpolyobj_t *link = ss->renderPolyList; + rpolyobj_t *rpo; + + while(link) + { + if(link->polyobj == po) + return link; + + link = (rpolyobj_t *)(link->link.next); + } + + // there is not one, so create a new one and link it in + rpo = R_GetFreeRPolyObj(); + + rpo->polyobj = po; + + M_DLListInsert(&rpo->link, (mdllistitem_t **)(void *)(&ss->renderPolyList)); + + return rpo; +} + +// +// Calculates dynaseg offset using the originating seg's dynavertices. +// +static void R_calcDynaSegOffset(dynaseg_t *dynaseg, int side) +{ + fixed_t dx = (side ? dynaseg->linev2->x : dynaseg->linev1->x) - dynaseg->seg.v1->x; + fixed_t dy = (side ? dynaseg->linev2->y : dynaseg->linev1->y) - dynaseg->seg.v1->y; + dynaseg->seg.offset = FixedHypot(dx>>1, dy>>1)<<1; +} + +// +// R_CreateDynaSeg +// +// Gets a new dynaseg and initializes it with all needed information. +// +dynaseg_t *R_CreateDynaSeg(const dynaseg_t *proto, dynavertex_t *v1, dynavertex_t *v2) +{ + dynaseg_t *ret = R_GetFreeDynaSeg(); + + // properties inherited from prototype seg + ret->polyobj = proto->polyobj; + ret->seg.linedef = proto->seg.linedef; + ret->seg.sidedef = proto->seg.sidedef; + ret->seg.side = proto->seg.side; + + ret->linev1 = proto->linev1; + ret->linev2 = proto->linev2; + + // vertices + R_SetDynaVertexRef(&ret->seg.dyv1, v1); + R_SetDynaVertexRef(&ret->seg.dyv2, v2); + + // calculate texture offset + R_calcDynaSegOffset(ret, ret->seg.side); + + return ret; +} + +// +// R_IntersectPoint +// +// Finds the point where a node line crosses a seg. +// +static boolean R_IntersectPoint(const seg_t *lseg, const node_t *node, dynavertex_t *nv) +{ + // get the fnode for the node + fnode_t *bsp = &fnodes[node - nodes]; + + double a1 = FixedToFloat(lseg->v2->y) - FixedToFloat(lseg->v1->y); + double b1 = FixedToFloat(lseg->v1->x) - FixedToFloat(lseg->v2->x); + double c1 = FixedToFloat(lseg->v2->x) * FixedToFloat(lseg->v1->y) - FixedToFloat(lseg->v1->x) * FixedToFloat(lseg->v2->y); + + // haleyjd 05/13/09: massive optimization + double a2 = -bsp->a; + double b2 = -bsp->b; + double c2 = -bsp->c; + + double d = a1 * b2 - a2 * b1; + float fx, fy; + + // lines are parallel?? shouldn't be. + // FIXME: could this occur due to roundoff error in R_PointOnSide? + // Guess we'll find out the hard way ;) + // If so I'll need my own R_PointOnSide routine with some + // epsilon values. + if(d == 0.0) + return false; + + fx = (float)((b1 * c2 - b2 * c1) / d); + fy = (float)((a2 * c1 - a1 * c2) / d); + + // set fixed-point coordinates + nv->x = FloatToFixed(fx); + nv->y = FloatToFixed(fy); + + return true; +} + +// +// R_PartitionDistance +// +// This routine uses the general line equation, whose coefficients are now +// precalculated in the BSP nodes, to determine the distance of the point +// from the partition line. If the distance is too small, we may decide to +// change our idea of sidedness. +// +static inline double R_PartitionDistance(double x, double y, const fnode_t *node) +{ + return fabs((node->a * x + node->b * y + node->c) / node->len); +} + +#define DS_EPSILON 0.3125 + +// +// Checks if seg is on top of a partition line +// +static boolean R_segIsOnPartition(const seg_t *seg, const subsector_t *frontss) +{ + const line_t *line; + float midpx, midpy; + int sign; + + if(seg->backsector) + return true; + + line = seg->linedef; + sign = line->frontsector == seg->frontsector ? 1 : -1; + + midpx = (float)((FixedToFloat(seg->v1->x) + FixedToFloat(seg->v2->x)) / 2 - line->nx * DS_EPSILON * sign); + midpy = (float)((FixedToFloat(seg->v1->y) + FixedToFloat(seg->v2->y)) / 2 - line->ny * DS_EPSILON * sign); + + return (R_PointInSubsector(FloatToFixed(midpx), FloatToFixed(midpy)) != frontss); +} + +// +// Checks the subsector for any wall segs which should cut or totally remove dseg. +// Necessary to avoid polyobject bleeding. Returns true if entire dynaseg is gone. +// +static boolean R_cutByWallSegs(dynaseg_t *dseg, dynaseg_t *backdseg, const subsector_t *ss) +{ + INT32 i; + + // The dynaseg must be in front of all wall segs. Otherwise, it's considered + // hidden behind walls. + seg_t *lseg = &dseg->seg; + dseg->psx = FixedToFloat(lseg->v1->x); + dseg->psy = FixedToFloat(lseg->v1->y); + dseg->pex = FixedToFloat(lseg->v2->x); + dseg->pey = FixedToFloat(lseg->v2->y); + + // Fast access to delta x, delta y + dseg->pdx = dseg->pex - dseg->psx; + dseg->pdy = dseg->pey - dseg->psy; + + for(i = 0; i < ss->numlines; ++i) + { + const seg_t *wall = &segs[ss->firstline + i]; + const vertex_t *v1 = wall->v1; + const vertex_t *v2 = wall->v2; + const divline_t walldl = { v1->x, v1->y, v2->x - v1->x, v2->y - v1->y }; + + int side_v1, side_v2; + dynaseg_t part; // this shall be the wall + + double vx, vy; + dynavertex_t *nv; + + if(R_segIsOnPartition(wall, ss)) + continue; // only check 1-sided lines + + side_v1 = P_PointOnDivlineSidePrecise(lseg->v1->x, lseg->v1->y, &walldl); + side_v2 = P_PointOnDivlineSidePrecise(lseg->v2->x, lseg->v2->y, &walldl); + + if(side_v1 == 0 && side_v2 == 0) + continue; // this one is fine. + if(side_v1 == 1 && side_v2 == 1) + return true; // totally occluded by one + + // We have a real intersection: cut it now. + part.psx = FixedToFloat(wall->v1->x); + part.psy = FixedToFloat(wall->v1->y); + part.pex = FixedToFloat(wall->v2->x); + part.pey = FixedToFloat(wall->v2->y); + + R_ComputeIntersection(&part, dseg, &vx, &vy); + + nv = R_GetFreeDynaVertex(); + nv->x = FloatToFixed(vx); + nv->y = FloatToFixed(vy); + + if(side_v1 == 0) + { + R_SetDynaVertexRef(&lseg->dyv2, nv); + if(backdseg) + { + R_SetDynaVertexRef(&backdseg->seg.dyv1, nv); + R_calcDynaSegOffset(backdseg, 1); + } + } + else + { + R_SetDynaVertexRef(&lseg->dyv1, nv); + R_calcDynaSegOffset(dseg, 0); // also need to update this + if(backdseg) + R_SetDynaVertexRef(&backdseg->seg.dyv2, nv); + } + // Keep looking for other intersectors + } + return false; // all are in front. So return. +} + +// +// R_SplitLine +// +// Given a single dynaseg representing the full length of a linedef, generates a +// set of dynasegs by recursively splitting the line through the BSP tree. +// Also does the same for a back dynaseg for 2-sided lines. +// +static void R_SplitLine(dynaseg_t *dseg, dynaseg_t *backdseg, int bspnum) +{ + int num; + rpolyobj_t *fragment; + + while(!(bspnum & NF_SUBSECTOR)) + { + const node_t *bsp = &nodes[bspnum]; + const fnode_t *fnode = &fnodes[bspnum]; + const seg_t *lseg = &dseg->seg; + + // test vertices against node line + int side_v1 = R_PointOnSide(lseg->v1->x, lseg->v1->y, bsp); + int side_v2 = R_PointOnSide(lseg->v2->x, lseg->v2->y, bsp); + + // ioanch 20160226: fix the polyobject visual clipping bug + M_AddToBox(bsp->bbox[side_v1], lseg->v1->x, lseg->v1->y); + M_AddToBox(bsp->bbox[side_v2], lseg->v2->x, lseg->v2->y); + + // get distance of vertices from partition line + double dist_v1 = R_PartitionDistance(FixedToFloat(lseg->v1->x), FixedToFloat(lseg->v1->y), fnode); + double dist_v2 = R_PartitionDistance(FixedToFloat(lseg->v2->x), FixedToFloat(lseg->v2->y), fnode); + + // If the distances are less than epsilon, consider the points as being + // on the same side as the polyobj origin. Why? People like to build + // polyobject doors flush with their door tracks. This breaks using the + // usual assumptions. + if(dist_v1 <= DS_EPSILON) + { + if(dist_v2 <= DS_EPSILON) + { + // both vertices are within epsilon distance; classify the seg + // with respect to the polyobject center point + side_v1 = side_v2 = R_PointOnSide(dseg->polyobj->centerPt.x, dseg->polyobj->centerPt.y, bsp); + } + else + side_v1 = side_v2; // v1 is very close; classify as v2 side + } + else if(dist_v2 <= DS_EPSILON) + { + side_v2 = side_v1; // v2 is very close; classify as v1 side + } + + if(side_v1 != side_v2) + { + // the partition line crosses this seg, so we must split it. + dynavertex_t *nv = R_GetFreeDynaVertex(); + dynaseg_t *nds; + + if(R_IntersectPoint(lseg, bsp, nv)) + { + dynaseg_t *backnds; + + // ioanch 20160722: fix the polyobject visual clipping bug (more needed) + M_AddToBox(bsp->bbox[0], nv->x, nv->y); + M_AddToBox(bsp->bbox[1], nv->x, nv->y); + + // create new dynaseg from nv to seg->v2 + nds = R_CreateDynaSeg(dseg, nv, lseg->dyv2); + + // alter current seg to run from seg->v1 to nv + R_SetDynaVertexRef(&lseg->dyv2, nv); + + if(backdseg) + { + backnds = R_CreateDynaSeg(backdseg, backdseg->seg.dyv1, nv); + R_SetDynaVertexRef(&backdseg->seg.dyv1, nv); + R_calcDynaSegOffset(backdseg, 1); + } + else + backnds = NULL; + + // recurse to split v2 side + R_SplitLine(nds, backnds, bsp->children[side_v2]); + } + else + { + // Classification failed (this really should not happen, but, math + // on computers is not ideal...). Return the dynavertex and do + // nothing here; the seg will be classified on v1 side for lack of + // anything better to do with it. + R_FreeDynaVertex(&nv); + } + } + + // continue on v1 side + bspnum = bsp->children[side_v1]; + } + + // reached a subsector: attach dynaseg + num = bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR; + +#ifdef RANGECHECK + if(num >= numsubsectors) + I_Error("R_SplitLine: ss %d with numss = %d\n", num, numsubsectors); +#endif + + // First, cut it off by any wall segs + if(R_cutByWallSegs(dseg, backdseg, &subsectors[num])) + { + // If it's occluded by everything, cancel it. + R_FreeDynaSeg(dseg); + if(backdseg) + R_FreeDynaSeg(backdseg); + return; + } + + // see if this subsector already has an rpolyobj_t for this polyobject + // if it does not, then one will be created. + fragment = R_FindFragment(&subsectors[num], dseg->polyobj); + + // link this seg in at the end of the list in the rpolyobj_t + if(fragment->dynaSegs) + { + dynaseg_t *fdseg = fragment->dynaSegs; + + while(fdseg->subnext) + fdseg = fdseg->subnext; + + fdseg->subnext = dseg; + } + else + fragment->dynaSegs = dseg; + dseg->subnext = backdseg; + + // 05/13/09: calculate seg length for SoM + P_CalcDynaSegLength(dseg); + if(backdseg) + backdseg->seg.length = dseg->seg.length; + + // 07/15/09: rendering consistency - set frontsector/backsector here + dseg->seg.polysector = subsectors[num].sector; + dseg->seg.frontsector = dseg->seg.linedef->frontsector; + + // 10/30/09: only set backsector if line is 2S + if(dseg->seg.linedef->backsector) + dseg->seg.backsector = dseg->seg.linedef->backsector; + else + dseg->seg.backsector = NULL; + + if(backdseg) + { + backdseg->seg.polysector = subsectors[num].sector; + backdseg->seg.frontsector = dseg->seg.linedef->frontsector; + backdseg->seg.backsector = dseg->seg.linedef->backsector; + } + + // add the subsector if it hasn't been added already + R_AddDynaSubsec(&subsectors[num], dseg->polyobj); +} + +// +// R_AttachPolyObject +// +// Generates dynamic segs for a single polyobject. +// +void R_AttachPolyObject(polyobj_t *poly) +{ + size_t i; + + // iterate on the polyobject lines array + for(i = 0; i < poly->numLines; ++i) + { + line_t *line = poly->lines[i]; + side_t *side = &sides[line->sidenum[0]]; + dynaseg_t *backdseg; + + // create initial dseg representing the entire linedef + dynaseg_t *idseg = R_GetFreeDynaSeg(); + + dynavertex_t *v1 = R_GetFreeDynaVertex(); + dynavertex_t *v2 = R_GetFreeDynaVertex(); + + memcpy(v1, line->v1, sizeof(vertex_t)); + memcpy(v2, line->v2, sizeof(vertex_t)); + + idseg->polyobj = poly; + idseg->seg.linedef = line; + idseg->seg.sidedef = side; + + R_SetDynaVertexRef(&idseg->seg.dyv1, v1); + R_SetDynaVertexRef(&idseg->seg.dyv2, v2); + idseg->linev1 = line->v1; + idseg->linev2 = line->v2; + + // create backside dynaseg now + if (!(poly->flags & POF_ONESIDE)) + { + backdseg = R_GetFreeDynaSeg(); + backdseg->polyobj = poly; + backdseg->seg.side = 1; + backdseg->seg.linedef = line; + backdseg->seg.sidedef = side; + R_SetDynaVertexRef(&backdseg->seg.dyv1, v2); + R_SetDynaVertexRef(&backdseg->seg.dyv2, v1); + backdseg->linev1 = line->v1; + backdseg->linev2 = line->v2; + } + else + backdseg = NULL; + + // Split seg into BSP tree to generate more dynasegs; + // The dynasegs are stored in the subsectors in which they finally end up. + R_SplitLine(idseg, backdseg, numnodes - 1); + } + + poly->attached = true; +} + +// +// R_DetachPolyObject +// +// Removes a polyobject from all subsectors to which it is attached, reclaiming +// all dynasegs, vertices, and rpolyobj_t fragment objects associated with the +// given polyobject. +// +void R_DetachPolyObject(polyobj_t *poly) +{ + int i; + + // no dynaseg-containing subsecs? + if(!poly->dynaSubsecs || !poly->numDSS) + { + poly->attached = false; + return; + } + + // iterate over stored subsector pointers + for(i = 0; i < poly->numDSS; ++i) + { + subsector_t *ss = poly->dynaSubsecs[i]; + rpolyobj_t *link = ss->renderPolyList; + rpolyobj_t *next; + + // mark BSPs dirty + if(ss->bsp) + ss->bsp->dirty = true; + + // iterate on subsector rpolyobj_t lists + while(link) + { + rpolyobj_t *rpo = link; + next = (rpolyobj_t *)(rpo->link.next); + + if(rpo->polyobj == poly) + { + // iterate on segs in rpolyobj_t + while(rpo->dynaSegs) + { + dynaseg_t *ds = rpo->dynaSegs; + dynaseg_t *nextds = ds->subnext; + + // free dynamic vertices + // put this dynaseg on the free list + R_FreeDynaSeg(ds); + + rpo->dynaSegs = nextds; + } + + // unlink this rpolyobj_t + M_DLListRemove(&link->link); + + // put it on the freelist + R_FreeRPolyObj(rpo); + } + + link = next; + } + + // no longer tracking this subsector + poly->dynaSubsecs[i] = NULL; + } + + // no longer tracking any subsectors + poly->numDSS = 0; + poly->attached = false; +} + +// +// R_ClearDynaSegs +// +// Call at the end of a level to clear all dynasegs. +// +// If this were not done, all dynasegs, their vertices, and polyobj fragments +// would be lost. +// +void R_ClearDynaSegs(void) +{ + size_t i; + + for(i = 0; i < (unsigned)numPolyObjects; i++) + R_DetachPolyObject(&PolyObjects[i]); + + for(i = 0; i < numsubsectors; i++) + { + if(subsectors[i].bsp) + R_FreeDynaBSP(subsectors[i].bsp); + } +} + +// EOF diff --git a/src/r_dynseg.h b/src/r_dynseg.h new file mode 100644 index 000000000..f0586fb2c --- /dev/null +++ b/src/r_dynseg.h @@ -0,0 +1,117 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2013 James Haley et al. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +// Additional terms and conditions compatible with the GPLv3 apply. See the +// file COPYING-EE for details. +// +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Dynamic segs for PolyObject re-implementation. +// +//----------------------------------------------------------------------------- + +#ifndef R_DYNSEG_H__ +#define R_DYNSEG_H__ + +#include "r_defs.h" +#include "m_dllist.h" +#include "p_polyobj.h" + +typedef struct dseglink_s +{ + mdllistitem_t link; + struct dynaseg_s *dynaseg; +} dseglink_t; + +typedef struct dynavertex_s // : vertex_t +{ + fixed_t x, y; + boolean floorzset, ceilingzset; + fixed_t floorz, ceilingz; + struct dynavertex_s *dynanext; + int refcount; +} dynavertex_t; + +// +// dynaseg +// +typedef struct dynaseg_s +{ + seg_t seg; // a dynaseg is a seg, after all ;) + + dynavertex_t *originalv2; // reference to original v2 before a split + vertex_t *linev1, *linev2; // dynavertices belonging to the endpoint segs + + struct dynaseg_s *subnext; // next dynaseg in fragment + struct dynaseg_s *freenext; // next dynaseg on freelist + polyobj_t *polyobj; // polyobject + + dseglink_t ownerlink; // link for owning node chain + dseglink_t alterlink; // link for non-dynaBSP segs changed by dynaBSP + + float prevlen, prevofs; // for interpolation (keep them out of seg_t) + + // properties needed for efficiency in the BSP builder + double psx, psy, pex, pey; // end points + double pdx, pdy; // delta x, delta y + double ptmp; // general line coefficient 'c' + double len; // length +} dynaseg_t; + +// Replaced dseglist_t with a different linked list implementation. +typedef struct dsegnode_s +{ + dynaseg_t *dynaseg; + struct dsegnode_s *prev, *next; +} dsegnode_t; + +// +// rpolyobj_t +// +// Subsectors now hold pointers to rpolyobj_t's instead of to polyobj_t's. +// An rpolyobj_t is a set of dynasegs belonging to a single polyobject. +// It is necessary to keep dynasegs belonging to different polyobjects +// separate from each other so that the renderer can continue to efficiently +// support multiple polyobjects per subsector (we do not want to do a z-sort +// on every single dynaseg, as that is significant unnecessary overhead). +// +typedef struct rpolyobj_s +{ + mdllistitem_t link; // for subsector links; must be first + + dynaseg_t *dynaSegs; // list of dynasegs + polyobj_t *polyobj; // polyobject of which this rpolyobj_t is a fragment + struct rpolyobj_s *freenext; // next on freelist +} rpolyobj_t; + +void P_CalcDynaSegLength(dynaseg_t *lseg); + +dynavertex_t *R_GetFreeDynaVertex(void); +void R_FreeDynaVertex(dynavertex_t **vtx); +void R_SetDynaVertexRef(dynavertex_t **target, dynavertex_t *vtx); +dynaseg_t *R_CreateDynaSeg(const dynaseg_t *proto, dynavertex_t *v1, dynavertex_t *v2); +void R_FreeDynaSeg(dynaseg_t *dseg); + +void R_AttachPolyObject(polyobj_t *poly); +void R_DetachPolyObject(polyobj_t *poly); +void R_ClearDynaSegs(void); + +#endif + +// EOF diff --git a/src/r_segs.c b/src/r_segs.c index a8c85ec33..9472291b8 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -140,13 +140,9 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) INT64 overflow_test; INT32 range; - // Calculate light table. - // Use different light tables - // for horizontal / vertical / diagonal. Diagonal? - // OPTIMIZE: get rid of LIGHTSEGSHIFT globally curline = ds->curline; - frontsector = curline->frontsector; + frontsector = curline->polyseg ? curline->polysector : curline->frontsector; backsector = curline->backsector; texnum = R_GetTextureNum(curline->sidedef->midtexture); windowbottom = windowtop = sprbotscreen = INT32_MAX; @@ -159,7 +155,6 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) { dc_transmap = R_GetTranslucencyTable(R_GetLinedefTransTable(ldef->alpha)); colfunc = colfuncs[COLDRAWFUNC_FUZZY]; - } else if (ldef->special == 909) { @@ -229,8 +224,6 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) rlight->height = (centeryfrac) - FixedMul(leftheight , ds->scale1); rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2); rlight->heightstep = (rlight->heightstep-rlight->height)/(range); - //if (x1 > ds->x1) - //rlight->height -= (x1 - ds->x1)*rlight->heightstep; rlight->startheight = rlight->height; // keep starting value here to reset for each repeat rlight->lightlevel = *light->lightlevel; rlight->extra_colormap = *light->extra_colormap; @@ -255,6 +248,10 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) } else { + // Calculate light table. + // Use different light tables + // for horizontal / vertical / diagonal. Diagonal? + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); diff --git a/src/r_state.h b/src/r_state.h index 5a606ed8c..7f9b46fbb 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -72,6 +72,7 @@ extern subsector_t *subsectors; extern size_t numnodes; extern node_t *nodes; +extern fnode_t *fnodes; extern size_t numlines; extern line_t *lines;