mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-24 13:21:20 +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_bsp.c
|
||||||
r_data.c
|
r_data.c
|
||||||
r_draw.c
|
r_draw.c
|
||||||
|
r_dynabsp.c
|
||||||
|
r_dynseg.c
|
||||||
r_main.c
|
r_main.c
|
||||||
r_plane.c
|
r_plane.c
|
||||||
r_segs.c
|
r_segs.c
|
||||||
|
@ -148,6 +150,8 @@ set(SRB2_CORE_RENDER_SOURCES
|
||||||
r_data.h
|
r_data.h
|
||||||
r_defs.h
|
r_defs.h
|
||||||
r_draw.h
|
r_draw.h
|
||||||
|
r_dynabsp.h
|
||||||
|
r_dynseg.h
|
||||||
r_local.h
|
r_local.h
|
||||||
r_main.h
|
r_main.h
|
||||||
r_plane.h
|
r_plane.h
|
||||||
|
|
|
@ -531,6 +531,8 @@ OBJS:=$(i_main_o) \
|
||||||
$(OBJDIR)/r_bsp.o \
|
$(OBJDIR)/r_bsp.o \
|
||||||
$(OBJDIR)/r_data.o \
|
$(OBJDIR)/r_data.o \
|
||||||
$(OBJDIR)/r_draw.o \
|
$(OBJDIR)/r_draw.o \
|
||||||
|
$(OBJDIR)/r_dynabsp.o\
|
||||||
|
$(OBJDIR)/r_dynseg.o \
|
||||||
$(OBJDIR)/r_main.o \
|
$(OBJDIR)/r_main.o \
|
||||||
$(OBJDIR)/r_plane.o \
|
$(OBJDIR)/r_plane.o \
|
||||||
$(OBJDIR)/r_segs.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;
|
line_t *line = lseg->linedef;
|
||||||
|
|
||||||
if (lseg->glseg)
|
if (lseg->glseg || lseg->polyseg)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//x,y,dx,dy (like a divline)
|
//x,y,dx,dy (like a divline)
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "../r_patch.h"
|
#include "../r_patch.h"
|
||||||
#include "../r_picformats.h"
|
#include "../r_picformats.h"
|
||||||
#include "../r_bsp.h"
|
#include "../r_bsp.h"
|
||||||
|
#include "../r_dynseg.h"
|
||||||
#include "../d_clisrv.h"
|
#include "../d_clisrv.h"
|
||||||
#include "../w_wad.h"
|
#include "../w_wad.h"
|
||||||
#include "../z_zone.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));
|
M_Memcpy(gl_fakeline, po_ptrs[i]->segs[j], sizeof(seg_t));
|
||||||
|
|
||||||
// Now convert the line to float and add it to be rendered
|
// Now convert the line to float and add it to be rendered
|
||||||
pv1->x = FIXED_TO_FLOAT(gl_fakeline->v1->x);
|
pv1->x = FIXED_TO_FLOAT(gl_fakeline->dyv1->x);
|
||||||
pv1->y = FIXED_TO_FLOAT(gl_fakeline->v1->y);
|
pv1->y = FIXED_TO_FLOAT(gl_fakeline->dyv1->y);
|
||||||
pv2->x = FIXED_TO_FLOAT(gl_fakeline->v2->x);
|
pv2->x = FIXED_TO_FLOAT(gl_fakeline->dyv2->x);
|
||||||
pv2->y = FIXED_TO_FLOAT(gl_fakeline->v2->y);
|
pv2->y = FIXED_TO_FLOAT(gl_fakeline->dyv2->y);
|
||||||
|
|
||||||
gl_fakeline->pv1 = pv1;
|
gl_fakeline->pv1 = pv1;
|
||||||
gl_fakeline->pv2 = pv2;
|
gl_fakeline->pv2 = pv2;
|
||||||
|
@ -3227,7 +3228,7 @@ static void HWR_Subsector(size_t num)
|
||||||
|
|
||||||
numpolys = 0;
|
numpolys = 0;
|
||||||
|
|
||||||
// Count all the polyobjects, reset the list, and recount them
|
// Count all the polyobjects
|
||||||
while (po)
|
while (po)
|
||||||
{
|
{
|
||||||
++numpolys;
|
++numpolys;
|
||||||
|
|
|
@ -50,8 +50,13 @@ FUNCINLINE static ATTRINLINE void M_DLListRemove(mdllistitem_t *item)
|
||||||
mdllistitem_t **prev = item->prev;
|
mdllistitem_t **prev = item->prev;
|
||||||
mdllistitem_t *next = item->next;
|
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;
|
next->prev = prev;
|
||||||
|
|
||||||
|
// haleyjd 05/07/13: safety #2: clear links.
|
||||||
|
item->prev = NULL;
|
||||||
|
item->next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -241,6 +241,37 @@ static INT32 P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t *line)
|
||||||
return 1; // back side
|
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
|
// 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_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);
|
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_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_MakeDivline(line_t *li, divline_t *dl);
|
||||||
void P_CameraLineOpening(line_t *plinedef);
|
void P_CameraLineOpening(line_t *plinedef);
|
||||||
fixed_t P_InterceptVector(divline_t *v2, divline_t *v1);
|
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...
|
{ // Water removes electric and non-water fire shields...
|
||||||
if (electric)
|
if (electric)
|
||||||
P_FlashPal(p, PAL_WHITE, 1);
|
P_FlashPal(p, PAL_WHITE, 1);
|
||||||
|
|
||||||
p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK;
|
p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "r_main.h"
|
#include "r_main.h"
|
||||||
#include "r_state.h"
|
#include "r_state.h"
|
||||||
#include "r_defs.h"
|
#include "r_defs.h"
|
||||||
|
#include "r_dynseg.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Theory behind Polyobjects:
|
Theory behind Polyobjects:
|
||||||
|
@ -601,7 +602,7 @@ static void Polyobj_moveToSpawnSpot(mapthing_t *anchor)
|
||||||
// Attaches a polyobject to its appropriate subsector.
|
// Attaches a polyobject to its appropriate subsector.
|
||||||
static void Polyobj_attachToSubsec(polyobj_t *po)
|
static void Polyobj_attachToSubsec(polyobj_t *po)
|
||||||
{
|
{
|
||||||
subsector_t *ss;
|
subsector_t *ss;
|
||||||
fixed_t center_x = 0, center_y = 0;
|
fixed_t center_x = 0, center_y = 0;
|
||||||
fixed_t numVertices;
|
fixed_t numVertices;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -610,6 +611,10 @@ static void Polyobj_attachToSubsec(polyobj_t *po)
|
||||||
if (po->isBad)
|
if (po->isBad)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// already attached?
|
||||||
|
if (po->attached)
|
||||||
|
return;
|
||||||
|
|
||||||
numVertices = (fixed_t)(po->numVertices*FRACUNIT);
|
numVertices = (fixed_t)(po->numVertices*FRACUNIT);
|
||||||
|
|
||||||
for (i = 0; i < po->numVertices; ++i)
|
for (i = 0; i < po->numVertices; ++i)
|
||||||
|
@ -622,25 +627,24 @@ static void Polyobj_attachToSubsec(polyobj_t *po)
|
||||||
po->centerPt.y = center_y;
|
po->centerPt.y = center_y;
|
||||||
|
|
||||||
ss = R_PointInSubsector(po->centerPt.x, po->centerPt.y);
|
ss = R_PointInSubsector(po->centerPt.x, po->centerPt.y);
|
||||||
|
|
||||||
M_DLListInsert(&po->link, (mdllistitem_t **)(void *)(&ss->polyList));
|
M_DLListInsert(&po->link, (mdllistitem_t **)(void *)(&ss->polyList));
|
||||||
|
|
||||||
#ifdef R_LINKEDPORTALS
|
R_AttachPolyObject(po);
|
||||||
// set spawnSpot's groupid for correct portal sound behavior
|
|
||||||
po->spawnSpot.groupid = ss->sector->groupid;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
po->attached = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a polyobject from the subsector to which it is attached.
|
// Removes a polyobject from the subsector to which it is attached.
|
||||||
static void Polyobj_removeFromSubsec(polyobj_t *po)
|
static void Polyobj_removeFromSubsec(polyobj_t *po)
|
||||||
{
|
{
|
||||||
if (po->attached)
|
// a bad polyobject should never have been attached in the first place
|
||||||
{
|
if (po->isBad)
|
||||||
M_DLListRemove(&po->link);
|
return;
|
||||||
po->attached = false;
|
|
||||||
}
|
// not attached?
|
||||||
|
if (!po->attached)
|
||||||
|
return;
|
||||||
|
|
||||||
|
M_DLListRemove(&po->link);
|
||||||
|
R_DetachPolyObject(po);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blockmap Functions
|
// Blockmap Functions
|
||||||
|
|
|
@ -76,8 +76,12 @@ typedef struct polyobj_s
|
||||||
|
|
||||||
INT32 parent; // numeric id of parent polyobject
|
INT32 parent; // numeric id of parent polyobject
|
||||||
|
|
||||||
size_t segCount; // number of segs in polyobject
|
struct subsector_s **dynaSubsecs; // list of subsectors holding fragments
|
||||||
size_t numSegsAlloc; // number of segs allocated
|
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.
|
struct seg_s **segs; // the segs, a reallocating array.
|
||||||
|
|
||||||
size_t numVertices; // number of vertices (generally == segCount)
|
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 numLines; // number of linedefs (generally <= segCount)
|
||||||
size_t numLinesAlloc; // number of linedefs allocated
|
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
|
degenmobj_t spawnSpot; // location of spawn spot
|
||||||
vertex_t centerPt; // center point
|
vertex_t centerPt; // center point
|
||||||
fixed_t zdist; // viewz distance for sorting
|
fixed_t zdist; // viewz distance for sorting
|
||||||
angle_t angle; // for rotation
|
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
|
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
|
size_t validcount; // for clipping: prevents multiple checks
|
||||||
INT32 damage; // damage to inflict on stuck things
|
INT32 damage; // damage to inflict on stuck things
|
||||||
fixed_t thrust; // amount of thrust to put on blocking objects
|
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
|
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
|
INT32 translucency; // index to translucency tables
|
||||||
INT16 triggertag; // Tag of linedef executor to trigger on touch
|
INT16 triggertag; // Tag of linedef executor to trigger on touch
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "r_picformats.h"
|
#include "r_picformats.h"
|
||||||
#include "r_sky.h"
|
#include "r_sky.h"
|
||||||
#include "r_draw.h"
|
#include "r_draw.h"
|
||||||
|
#include "r_dynseg.h"
|
||||||
|
|
||||||
#include "s_sound.h"
|
#include "s_sound.h"
|
||||||
#include "st_stuff.h"
|
#include "st_stuff.h"
|
||||||
|
@ -103,6 +104,7 @@ seg_t *segs;
|
||||||
sector_t *sectors;
|
sector_t *sectors;
|
||||||
subsector_t *subsectors;
|
subsector_t *subsectors;
|
||||||
node_t *nodes;
|
node_t *nodes;
|
||||||
|
fnode_t *fnodes;
|
||||||
line_t *lines;
|
line_t *lines;
|
||||||
side_t *sides;
|
side_t *sides;
|
||||||
mapthing_t *mapthings;
|
mapthing_t *mapthings;
|
||||||
|
@ -948,6 +950,12 @@ void P_WriteThings(void)
|
||||||
// MAP LOADING FUNCTIONS
|
// 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)
|
static void P_LoadVertices(UINT8 *data)
|
||||||
{
|
{
|
||||||
mapvertex_t *mv = (mapvertex_t *)data;
|
mapvertex_t *mv = (mapvertex_t *)data;
|
||||||
|
@ -959,8 +967,7 @@ static void P_LoadVertices(UINT8 *data)
|
||||||
{
|
{
|
||||||
v->x = SHORT(mv->x)<<FRACBITS;
|
v->x = SHORT(mv->x)<<FRACBITS;
|
||||||
v->y = SHORT(mv->y)<<FRACBITS;
|
v->y = SHORT(mv->y)<<FRACBITS;
|
||||||
v->floorzset = v->ceilingzset = false;
|
P_InitializeVertex(v);
|
||||||
v->floorz = v->ceilingz = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1136,6 +1143,23 @@ static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
|
||||||
lines[i].v2 = &vertexes[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)
|
static void P_LoadLinedefs(UINT8 *data)
|
||||||
{
|
{
|
||||||
maplinedef_t *mld = (maplinedef_t *)data;
|
maplinedef_t *mld = (maplinedef_t *)data;
|
||||||
|
@ -1158,6 +1182,7 @@ static void P_LoadLinedefs(UINT8 *data)
|
||||||
ld->sidenum[1] = SHORT(mld->sidenum[1]);
|
ld->sidenum[1] = SHORT(mld->sidenum[1]);
|
||||||
|
|
||||||
P_InitializeLinedef(ld);
|
P_InitializeLinedef(ld);
|
||||||
|
P_MakeLineNormal(ld);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1828,8 +1853,7 @@ static void P_LoadTextmap(void)
|
||||||
{
|
{
|
||||||
// Defaults.
|
// Defaults.
|
||||||
vt->x = vt->y = INT32_MAX;
|
vt->x = vt->y = INT32_MAX;
|
||||||
vt->floorzset = vt->ceilingzset = false;
|
P_InitializeVertex(vt);
|
||||||
vt->floorz = vt->ceilingz = 0;
|
|
||||||
|
|
||||||
TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
|
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));
|
I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
|
||||||
|
|
||||||
P_InitializeLinedef(ld);
|
P_InitializeLinedef(ld);
|
||||||
|
P_MakeLineNormal(ld);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0, sd = sides; i < numsides; i++, sd++)
|
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)
|
static void P_LoadNodes(UINT8 *data)
|
||||||
{
|
{
|
||||||
UINT8 j, k;
|
UINT8 j, k;
|
||||||
|
@ -2134,6 +2182,10 @@ static void P_LoadNodes(UINT8 *data)
|
||||||
no->y = SHORT(mn->y)<<FRACBITS;
|
no->y = SHORT(mn->y)<<FRACBITS;
|
||||||
no->dx = SHORT(mn->dx)<<FRACBITS;
|
no->dx = SHORT(mn->dx)<<FRACBITS;
|
||||||
no->dy = SHORT(mn->dy)<<FRACBITS;
|
no->dy = SHORT(mn->dy)<<FRACBITS;
|
||||||
|
|
||||||
|
// haleyjd: calculate floating-point data
|
||||||
|
P_CalcNodeCoefficients(no, &fnodes[i]);
|
||||||
|
|
||||||
for (j = 0; j < 2; j++)
|
for (j = 0; j < 2; j++)
|
||||||
{
|
{
|
||||||
no->children[j] = SHORT(mn->children[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.
|
* \param seg Seg to compute length for.
|
||||||
* \return Length in fracunits.
|
* \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 dx = (seg->v2->x - seg->v1->x)>>1;
|
||||||
INT64 dy = (seg->v2->y - seg->v1->y)>>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));
|
numnodes = READINT32((*data));
|
||||||
nodes = Z_Calloc(numnodes*sizeof(*nodes), PU_LEVEL, NULL);
|
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++)
|
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->dx = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
|
||||||
mn->dy = 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
|
// Bounding boxes
|
||||||
for (j = 0; j < 2; j++)
|
for (j = 0; j < 2; j++)
|
||||||
for (k = 0; k < 4; k++)
|
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);
|
subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL);
|
||||||
nodes = Z_Calloc(numnodes * sizeof(*nodes), 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);
|
segs = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL);
|
||||||
|
|
||||||
P_LoadSubsectors(virtssectors->data);
|
P_LoadSubsectors(virtssectors->data);
|
||||||
|
@ -3235,7 +3292,7 @@ static void P_ConvertBinaryMap(void)
|
||||||
lines[i].args[4] |= TMSC_BACKTOFRONTCEILING;
|
lines[i].args[4] |= TMSC_BACKTOFRONTCEILING;
|
||||||
lines[i].special = 720;
|
lines[i].special = 720;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 900: //Translucent wall (10%)
|
case 900: //Translucent wall (10%)
|
||||||
case 901: //Translucent wall (20%)
|
case 901: //Translucent wall (20%)
|
||||||
case 902: //Translucent wall (30%)
|
case 902: //Translucent wall (30%)
|
||||||
|
@ -4228,6 +4285,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
||||||
Z_Free(ss->attachedsolid);
|
Z_Free(ss->attachedsolid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// haleyjd 05/16/08: clear dynamic segs
|
||||||
|
R_ClearDynaSegs();
|
||||||
|
|
||||||
// Clear pointers that would be left dangling by the purge
|
// Clear pointers that would be left dangling by the purge
|
||||||
R_FlushTranslationColormapCache();
|
R_FlushTranslationColormapCache();
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,8 @@ INT32 P_CheckLevelFlat(const char *flatname);
|
||||||
extern size_t nummapthings;
|
extern size_t nummapthings;
|
||||||
extern mapthing_t *mapthings;
|
extern mapthing_t *mapthings;
|
||||||
|
|
||||||
|
fixed_t P_SegLength(seg_t *seg);
|
||||||
|
|
||||||
void P_SetupLevelSky(INT32 skynum, boolean global);
|
void P_SetupLevelSky(INT32 skynum, boolean global);
|
||||||
#ifdef SCANTHINGS
|
#ifdef SCANTHINGS
|
||||||
void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
|
void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
|
||||||
|
|
|
@ -5188,13 +5188,13 @@ static void P_PlayerOnSpecial3DFloor(player_t *player, sector_t *sector)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're inside it! Yess...
|
|
||||||
if (!polysec->special)
|
if (!polysec->special)
|
||||||
{
|
{
|
||||||
po = (polyobj_t *)(po->link.next);
|
po = (polyobj_t *)(po->link.next);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We're inside it! Yess...
|
||||||
if (!(po->flags & POF_TESTHEIGHT)) // Don't do height checking
|
if (!(po->flags & POF_TESTHEIGHT)) // Don't do height checking
|
||||||
;
|
;
|
||||||
else if (po->flags & POF_SOLID)
|
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_local.h"
|
||||||
#include "r_state.h"
|
#include "r_state.h"
|
||||||
#include "r_portal.h" // Add seg portals
|
#include "r_portal.h" // Add seg portals
|
||||||
|
#include "r_dynabsp.h"
|
||||||
|
#include "r_dynseg.h"
|
||||||
|
|
||||||
#include "r_splats.h"
|
#include "r_splats.h"
|
||||||
#include "p_local.h" // camera
|
#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
|
static void R_renderPolyNode(const rpolynode_t *node)
|
||||||
// closer one is sorted first. I sure hope this doesn't break anything. -Red
|
|
||||||
//
|
|
||||||
static int R_PolysegCompare(const void *p1, const void *p2)
|
|
||||||
{
|
{
|
||||||
const seg_t *seg1 = *(const seg_t * const *)p1;
|
while(node)
|
||||||
const seg_t *seg2 = *(const seg_t * const *)p2;
|
{
|
||||||
fixed_t dist1v1, dist1v2, dist2v1, dist2v2;
|
seg_t *seg = &node->partition->seg;
|
||||||
|
|
||||||
// TODO might be a better way to get distance?
|
// render frontspace
|
||||||
#define pdist(x, y) (FixedMul(R_PointToDist(x, y), FINECOSINE((R_PointToAngle(x, y)-viewangle)>>ANGLETOFINESHIFT))+0xFFFFFFF)
|
int side = R_PointOnDynaSegSide(node->partition, FixedToFloat(viewx), FixedToFloat(viewy));
|
||||||
#define vxdist(v) pdist(v->x, v->y)
|
R_renderPolyNode(node->children[side]);
|
||||||
|
|
||||||
dist1v1 = vxdist(seg1->v1);
|
// render partition seg
|
||||||
dist1v2 = vxdist(seg1->v2);
|
seg->angle = R_PointToAngleEx(seg->v1->x, seg->v1->y, seg->v2->x, seg->v2->y);
|
||||||
dist2v1 = vxdist(seg2->v1);
|
seg->polyseg = node->partition->polyobj;
|
||||||
dist2v2 = vxdist(seg2->v2);
|
R_AddLine(seg);
|
||||||
|
|
||||||
if (min(dist1v1, dist1v2) != min(dist2v1, dist2v2))
|
// continue to render backspace
|
||||||
return min(dist1v1, dist1v2) - min(dist2v1, dist2v2);
|
node = node->children[side^1];
|
||||||
|
|
||||||
{ // That didn't work, so now let's try this.......
|
|
||||||
fixed_t delta1, delta2, x1, y1, x2, y2;
|
|
||||||
vertex_t *near1, *near2, *far1, *far2; // wherever you are~
|
|
||||||
|
|
||||||
delta1 = R_PointToDist2(seg1->v1->x, seg1->v1->y, seg1->v2->x, seg1->v2->y);
|
|
||||||
delta2 = R_PointToDist2(seg2->v1->x, seg2->v1->y, seg2->v2->x, seg2->v2->y);
|
|
||||||
|
|
||||||
delta1 = FixedDiv(128<<FRACBITS, delta1);
|
|
||||||
delta2 = FixedDiv(128<<FRACBITS, delta2);
|
|
||||||
|
|
||||||
if (dist1v1 < dist1v2)
|
|
||||||
{
|
|
||||||
near1 = seg1->v1;
|
|
||||||
far1 = seg1->v2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
near1 = seg1->v2;
|
|
||||||
far1 = seg1->v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dist2v1 < dist2v2)
|
|
||||||
{
|
|
||||||
near2 = seg2->v1;
|
|
||||||
far2 = seg2->v2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
near2 = seg2->v2;
|
|
||||||
far2 = seg2->v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
x1 = near1->x + FixedMul(far1->x-near1->x, delta1);
|
|
||||||
y1 = near1->y + FixedMul(far1->y-near1->y, delta1);
|
|
||||||
|
|
||||||
x2 = near2->x + FixedMul(far2->x-near2->x, delta2);
|
|
||||||
y2 = near2->y + FixedMul(far2->y-near2->y, delta2);
|
|
||||||
|
|
||||||
return pdist(x1, y1)-pdist(x2, y2);
|
|
||||||
}
|
}
|
||||||
#undef vxdist
|
|
||||||
#undef pdist
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// R_AddPolyObjects
|
// haleyjd: 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
|
// See r_dynseg.c to see how dynasegs get attached to a subsector in the
|
||||||
// Adds all segs in all polyobjects in the given subsector.
|
// 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;
|
boolean needbsp = (!sub->bsp || sub->bsp->dirty);
|
||||||
size_t i, j;
|
|
||||||
|
|
||||||
numpolys = 0;
|
if(needbsp)
|
||||||
|
|
||||||
// count polyobjects
|
|
||||||
while (po)
|
|
||||||
{
|
{
|
||||||
++numpolys;
|
if(sub->bsp)
|
||||||
po = (polyobj_t *)(po->link.next);
|
R_FreeDynaBSP(sub->bsp);
|
||||||
|
sub->bsp = R_BuildDynaBSP(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for render stats
|
if(sub->bsp)
|
||||||
ps_numpolyobjects += numpolys;
|
R_renderPolyNode(sub->bsp->root);
|
||||||
|
|
||||||
// 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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -988,19 +937,21 @@ static void R_Subsector(size_t num)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polyobjects have planes, too!
|
// Polyobjects have planes, too!
|
||||||
if (sub->polyList)
|
if (sub->renderPolyList)
|
||||||
{
|
{
|
||||||
polyobj_t *po = sub->polyList;
|
rpolyobj_t *rpo = sub->renderPolyList;
|
||||||
sector_t *polysec;
|
sector_t *polysec;
|
||||||
|
|
||||||
while (po)
|
while (rpo)
|
||||||
{
|
{
|
||||||
|
polyobj_t *po = rpo->polyobj;
|
||||||
|
|
||||||
if (numffloors >= MAXFFLOORS)
|
if (numffloors >= MAXFFLOORS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!(po->flags & POF_RENDERPLANES)) // Don't draw planes
|
if (!(po->flags & POF_RENDERPLANES)) // Don't draw planes
|
||||||
{
|
{
|
||||||
po = (polyobj_t *)(po->link.next);
|
rpo = (rpolyobj_t *)(rpo->link.next);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1049,7 +1000,7 @@ static void R_Subsector(size_t num)
|
||||||
numffloors++;
|
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;
|
firstseg = NULL;
|
||||||
|
|
||||||
// haleyjd 02/19/06: draw polyobjects before static lines
|
// haleyjd 02/19/06: draw polyobjects before static lines
|
||||||
if (sub->polyList)
|
if (sub->renderPolyList)
|
||||||
R_AddPolyObjects(sub);
|
R_addDynaSegs(sub);
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
{
|
{
|
||||||
|
|
36
src/r_defs.h
36
src/r_defs.h
|
@ -24,10 +24,6 @@
|
||||||
|
|
||||||
#include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT
|
#include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT
|
||||||
|
|
||||||
#ifdef HWRENDER
|
|
||||||
#include "m_aatree.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "taglist.h"
|
#include "taglist.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -386,6 +382,7 @@ typedef struct line_s
|
||||||
vertex_t *v2;
|
vertex_t *v2;
|
||||||
|
|
||||||
fixed_t dx, dy; // Precalculated v2 - v1 for side checking.
|
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.
|
// Animation related.
|
||||||
INT16 flags;
|
INT16 flags;
|
||||||
|
@ -453,7 +450,9 @@ typedef struct subsector_s
|
||||||
sector_t *sector;
|
sector_t *sector;
|
||||||
INT16 numlines;
|
INT16 numlines;
|
||||||
UINT16 firstline;
|
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;
|
size_t validcount;
|
||||||
} subsector_t;
|
} subsector_t;
|
||||||
|
|
||||||
|
@ -523,8 +522,11 @@ typedef struct lightmap_s
|
||||||
//
|
//
|
||||||
typedef struct seg_s
|
typedef struct seg_s
|
||||||
{
|
{
|
||||||
vertex_t *v1;
|
union
|
||||||
vertex_t *v2;
|
{
|
||||||
|
struct { vertex_t *v1, *v2; };
|
||||||
|
struct { struct dynavertex_s *dyv1, *dyv2; };
|
||||||
|
};
|
||||||
|
|
||||||
INT32 side;
|
INT32 side;
|
||||||
|
|
||||||
|
@ -541,6 +543,7 @@ typedef struct seg_s
|
||||||
sector_t *backsector;
|
sector_t *backsector;
|
||||||
|
|
||||||
fixed_t length; // precalculated seg length
|
fixed_t length; // precalculated seg length
|
||||||
|
|
||||||
#ifdef HWRENDER
|
#ifdef HWRENDER
|
||||||
// new pointers so that AdjustSegs doesn't mess with v1/v2
|
// new pointers so that AdjustSegs doesn't mess with v1/v2
|
||||||
void *pv1; // polyvertex_t
|
void *pv1; // polyvertex_t
|
||||||
|
@ -550,10 +553,13 @@ typedef struct seg_s
|
||||||
lightmap_t *lightmaps; // for static lightmap
|
lightmap_t *lightmaps; // for static lightmap
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
polyobj_t *polyseg;
|
||||||
|
sector_t *polysector;
|
||||||
|
|
||||||
// Why slow things down by calculating lightlists for every thick side?
|
// Why slow things down by calculating lightlists for every thick side?
|
||||||
size_t numlights;
|
size_t numlights;
|
||||||
r_lightlist_t *rlights;
|
r_lightlist_t *rlights;
|
||||||
polyobj_t *polyseg;
|
|
||||||
boolean dontrenderme;
|
boolean dontrenderme;
|
||||||
boolean glseg;
|
boolean glseg;
|
||||||
} seg_t;
|
} seg_t;
|
||||||
|
@ -574,6 +580,20 @@ typedef struct
|
||||||
UINT16 children[2];
|
UINT16 children[2];
|
||||||
} node_t;
|
} 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)
|
#if defined(_MSC_VER)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
#endif
|
#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;
|
INT64 overflow_test;
|
||||||
INT32 range;
|
INT32 range;
|
||||||
|
|
||||||
// Calculate light table.
|
|
||||||
// Use different light tables
|
|
||||||
// for horizontal / vertical / diagonal. Diagonal?
|
|
||||||
// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
|
|
||||||
curline = ds->curline;
|
curline = ds->curline;
|
||||||
|
|
||||||
frontsector = curline->frontsector;
|
frontsector = curline->polyseg ? curline->polysector : curline->frontsector;
|
||||||
backsector = curline->backsector;
|
backsector = curline->backsector;
|
||||||
texnum = R_GetTextureNum(curline->sidedef->midtexture);
|
texnum = R_GetTextureNum(curline->sidedef->midtexture);
|
||||||
windowbottom = windowtop = sprbotscreen = INT32_MAX;
|
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));
|
dc_transmap = R_GetTranslucencyTable(R_GetLinedefTransTable(ldef->alpha));
|
||||||
colfunc = colfuncs[COLDRAWFUNC_FUZZY];
|
colfunc = colfuncs[COLDRAWFUNC_FUZZY];
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (ldef->special == 909)
|
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->height = (centeryfrac) - FixedMul(leftheight , ds->scale1);
|
||||||
rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2);
|
rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2);
|
||||||
rlight->heightstep = (rlight->heightstep-rlight->height)/(range);
|
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->startheight = rlight->height; // keep starting value here to reset for each repeat
|
||||||
rlight->lightlevel = *light->lightlevel;
|
rlight->lightlevel = *light->lightlevel;
|
||||||
rlight->extra_colormap = *light->extra_colormap;
|
rlight->extra_colormap = *light->extra_colormap;
|
||||||
|
@ -255,6 +248,10 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Calculate light table.
|
||||||
|
// Use different light tables
|
||||||
|
// for horizontal / vertical / diagonal. Diagonal?
|
||||||
|
// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
|
||||||
if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY])
|
if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY])
|
||||||
|| (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG)))
|
|| (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG)))
|
||||||
lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT);
|
lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT);
|
||||||
|
|
|
@ -72,6 +72,7 @@ extern subsector_t *subsectors;
|
||||||
|
|
||||||
extern size_t numnodes;
|
extern size_t numnodes;
|
||||||
extern node_t *nodes;
|
extern node_t *nodes;
|
||||||
|
extern fnode_t *fnodes;
|
||||||
|
|
||||||
extern size_t numlines;
|
extern size_t numlines;
|
||||||
extern line_t *lines;
|
extern line_t *lines;
|
||||||
|
|
Loading…
Reference in a new issue