mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-21 12:01:05 +00:00
Implement dynasegs, from Eternity
This commit is contained in:
parent
786516fa5f
commit
1fb70c7d12
21 changed files with 1883 additions and 141 deletions
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)<<FRACBITS;
|
||||
v->y = SHORT(mv->y)<<FRACBITS;
|
||||
v->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)<<FRACBITS;
|
||||
no->dx = SHORT(mn->dx)<<FRACBITS;
|
||||
no->dy = SHORT(mn->dy)<<FRACBITS;
|
||||
|
||||
// haleyjd: calculate floating-point data
|
||||
P_CalcNodeCoefficients(no, &fnodes[i]);
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
no->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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
133
src/r_bsp.c
133
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<<FRACBITS, delta1);
|
||||
delta2 = FixedDiv(128<<FRACBITS, delta2);
|
||||
|
||||
if (dist1v1 < dist1v2)
|
||||
{
|
||||
near1 = seg1->v1;
|
||||
far1 = seg1->v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
near1 = seg1->v2;
|
||||
far1 = seg1->v1;
|
||||
}
|
||||
|
||||
if (dist2v1 < dist2v2)
|
||||
{
|
||||
near2 = seg2->v1;
|
||||
far2 = seg2->v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
near2 = seg2->v2;
|
||||
far2 = seg2->v1;
|
||||
}
|
||||
|
||||
x1 = near1->x + FixedMul(far1->x-near1->x, delta1);
|
||||
y1 = near1->y + FixedMul(far1->y-near1->y, delta1);
|
||||
|
||||
x2 = near2->x + FixedMul(far2->x-near2->x, delta2);
|
||||
y2 = near2->y + FixedMul(far2->y-near2->y, delta2);
|
||||
|
||||
return pdist(x1, y1)-pdist(x2, y2);
|
||||
// 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--)
|
||||
{
|
||||
|
|
36
src/r_defs.h
36
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
|
||||
|
|
719
src/r_dynabsp.c
Normal file
719
src/r_dynabsp.c
Normal file
|
@ -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
|
62
src/r_dynabsp.h
Normal file
62
src/r_dynabsp.h
Normal file
|
@ -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
|
761
src/r_dynseg.c
Normal file
761
src/r_dynseg.c
Normal file
|
@ -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
|
117
src/r_dynseg.h
Normal file
117
src/r_dynseg.h
Normal file
|
@ -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
|
13
src/r_segs.c
13
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue