diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c index 8d2e40431..e0a3572b3 100644 --- a/src/hardware/hw_bsp.c +++ b/src/hardware/hw_bsp.c @@ -478,7 +478,7 @@ static void HWR_AddPolyObjectPlanes(void) sector_t *polyobjsector; INT32 light = 0; - // Polyobject Planes need their own function for drawing because they don't have extrasubsectors by themselves + // Polyobject Planes need their own function for drawing because they don't have poly_subsectors by themselves // It should be okay because polyobjects should always be convex anyway for (i = 0; i < numpolys; i++) @@ -567,9 +567,9 @@ static void HWR_Subsector(size_t num) // CONS_Printf("subsector %d in portal\n", (INT32)num); #ifdef PARANOIA //no risk while developing, enough debugging nights! - if (num >= addsubsector) + if (num >= num_poly_subsector) I_Error("HWR_Subsector: ss %s with numss = %s, addss = %s\n", - sizeu1(num), sizeu2(numsubsectors), sizeu3(addsubsector)); + sizeu1(num), sizeu2(numsubsectors), sizeu3(num_poly_subsector)); /*if (num >= numsubsectors) I_Error("HWR_Subsector: ss %i with numss = %i", @@ -668,7 +668,7 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetLevelFlat(&levelflats[gl_frontsector->floorpic], R_NoEncore(gl_frontsector, &levelflats[gl_frontsector->floorpic], false)); - HWR_RenderPlane(sub, &extrasubsectors[num], false, + HWR_RenderPlane(sub, &poly_subsectors[num], false, // Hack to make things continue to work around slopes. locFloorHeight == cullFloorHeight ? locFloorHeight : gl_frontsector->floorheight, // We now return you to your regularly scheduled rendering. @@ -685,7 +685,7 @@ static void HWR_Subsector(size_t num) if (sub->validcount != validcount) { HWR_GetLevelFlat(&levelflats[gl_frontsector->ceilingpic], R_NoEncore(gl_frontsector, &levelflats[gl_frontsector->ceilingpic], true)); - HWR_RenderPlane(sub, &extrasubsectors[num], true, + HWR_RenderPlane(sub, &poly_subsectors[num], true, // Hack to make things continue to work around slopes. locCeilingHeight == cullCeilingHeight ? locCeilingHeight : gl_frontsector->ceilingheight, // We now return you to your regularly scheduled rendering. @@ -725,7 +725,7 @@ static void HWR_Subsector(size_t num) alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, - &extrasubsectors[num], + &poly_subsectors[num], false, *rover->bottomheight, *gl_frontsector->lightlist[light].lightlevel, @@ -740,7 +740,7 @@ static void HWR_Subsector(size_t num) light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->bottompic], - &extrasubsectors[num], + &poly_subsectors[num], false, *rover->bottomheight, *gl_frontsector->lightlist[light].lightlevel, @@ -751,7 +751,7 @@ static void HWR_Subsector(size_t num) { HWR_GetLevelFlat(&levelflats[*rover->bottompic], R_NoEncore(gl_frontsector, &levelflats[*rover->bottompic], false)); light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); - HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic], + HWR_RenderPlane(sub, &poly_subsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } } @@ -773,7 +773,7 @@ static void HWR_Subsector(size_t num) alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, - &extrasubsectors[num], + &poly_subsectors[num], true, *rover->topheight, *gl_frontsector->lightlist[light].lightlevel, @@ -788,7 +788,7 @@ static void HWR_Subsector(size_t num) light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->toppic], - &extrasubsectors[num], + &poly_subsectors[num], true, *rover->topheight, *gl_frontsector->lightlist[light].lightlevel, @@ -799,7 +799,7 @@ static void HWR_Subsector(size_t num) { HWR_GetLevelFlat(&levelflats[*rover->toppic], R_NoEncore(gl_frontsector, &levelflats[*rover->toppic], true)); light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); - HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, true) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic], + HWR_RenderPlane(sub, &poly_subsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, true) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } } diff --git a/src/hardware/hw_drawnodes.c b/src/hardware/hw_drawnodes.c index f0030aeef..f48eec096 100644 --- a/src/hardware/hw_drawnodes.c +++ b/src/hardware/hw_drawnodes.c @@ -37,7 +37,7 @@ typedef struct typedef struct { - extrasubsector_t *xsub; + poly_subsector_t *xsub; boolean isceiling; fixed_t fixedheight; INT32 lightlevel; @@ -143,7 +143,7 @@ void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 te } // This will likely turn into a copy of HWR_Add3DWater and replace it. -void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) +void HWR_AddTransparentFloor(levelflat_t *levelflat, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap) { planeinfo_t *planeinfo = HWR_CreateDrawNode(DRAWNODE_PLANE); diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index af954d486..1ec637413 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -54,7 +54,7 @@ typedef struct typedef struct { poly_t *planepoly; // the generated convex polygon -} extrasubsector_t; +} poly_subsector_t; // needed for sprite rendering // equivalent of the software renderer's vissprites @@ -117,16 +117,14 @@ typedef struct gl_vissprite_s void HWR_ObjectLightLevelPost(gl_vissprite_t *spr, const sector_t *sector, INT32 *lightlevel, boolean model); // -------- -// hw_bsp.c +// hw_map.c // -------- -extern extrasubsector_t *extrasubsectors; -extern size_t addsubsector; +extern poly_subsector_t *poly_subsectors; +extern size_t num_poly_subsector; void HWR_InitPolyPool(void); void HWR_FreePolyPool(void); -void HWR_FreeExtraSubsectors(void); - // -------- // hw_cache.c // -------- @@ -176,7 +174,7 @@ extern INT32 textureformat; // -------- // hw_plane.c // -------- -void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap); +void HWR_RenderPlane(subsector_t *subsector, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap); void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, FBITFIELD blendmode, UINT8 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap); @@ -185,7 +183,7 @@ void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fixed_t // hw_drawnodes.c // -------- void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 texnum, boolean noencore, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap); -void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); +void HWR_AddTransparentFloor(levelflat_t *levelflat, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap); void HWR_PushDrawNodeState(void); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index e45f36ffb..71560d3a9 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -1255,6 +1255,17 @@ consvar_t cv_glanisotropicmode = CVAR_INIT ("gr_anisotropicmode", "1", CV_SAVE|C consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL); +consvar_t cv_glpolytile = CVAR_INIT ("gr_polytile", "On", 0, CV_OnOff, NULL); + +CV_PossibleValue_t grpolyshape_cons_t[] = { + {0, "Subsector"}, + {1, "Fat"}, + {2, "Trim"}, + {3, "NotConvex"}, + {0, NULL} +}; +consvar_t cv_glpolyshape = CVAR_INIT ("gr_polygon_shape", "Trim", CV_SAVE, grpolyshape_cons_t, NULL); + consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL); CV_PossibleValue_t glpalettedepth_cons_t[] = {{16, "16 bits"}, {24, "24 bits"}, {0, NULL}}; @@ -1365,7 +1376,10 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glallowshaders); CV_RegisterVar(&cv_glfiltermode); + CV_RegisterVar(&cv_glsolvetjoin); + CV_RegisterVar(&cv_glpolytile); + CV_RegisterVar(&cv_glpolyshape); CV_RegisterVar(&cv_glbatching); @@ -1399,6 +1413,7 @@ void HWR_Startup(void) textureformat = patchformat = GL_TEXFMT_RGBA; HWR_InitPolyPool(); + HWR_AddSessionCommands(); HWR_InitMapTextures(); HWR_InitModels(); @@ -1445,7 +1460,6 @@ void HWR_Switch(void) void HWR_Shutdown(void) { CONS_Printf("HWR_Shutdown()\n"); - HWR_FreeExtraSubsectors(); HWR_FreePolyPool(); HWR_FreeMapTextures(); GL_FlushScreenTextures(); diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 81e69fbfe..879f3c40b 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -110,11 +110,13 @@ extern consvar_t cv_glmodellighting; extern consvar_t cv_glfiltermode; extern consvar_t cv_glanisotropicmode; extern consvar_t cv_fovchange; -extern consvar_t cv_glsolvetjoin; + extern consvar_t cv_glshearing; extern consvar_t cv_glspritebillboarding; extern consvar_t cv_glskydome; +extern consvar_t cv_glsolvetjoin, cv_glpolytile, cv_glpolyshape; + extern consvar_t cv_glbatching; extern consvar_t cv_glpaletterendering; extern consvar_t cv_glpalettedepth; diff --git a/src/hardware/hw_map.c b/src/hardware/hw_map.c index ddfb262d1..ca6051b65 100644 --- a/src/hardware/hw_map.c +++ b/src/hardware/hw_map.c @@ -1,39 +1,627 @@ // BLANKART //----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2016 by DooM Legacy Team. +// Copyright (C) 1999-2019 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file hw_bsp.c +/// \file hw_map.c /// \brief convert SRB2 map -#include "../doomdef.h" -#include "../doomstat.h" #ifdef HWRENDER + +#include + #include "hw_glob.h" +#include "hw_main.h" #include "../r_local.h" #include "../z_zone.h" #include "../console.h" #include "../v_video.h" #include "../m_menu.h" #include "../i_system.h" -#include "../m_argv.h" #include "../i_video.h" -#include "../w_wad.h" -#include "../p_setup.h" // levelfadecol + +#define FIXED_TO_FLOAT_MULT (1.0f / 65536.0f) + +//#define DEBUG_HWBSP + +//#define DEBUG_TRACE +#ifdef DEBUG_TRACE +static int trigger_bsp_sector = 0xFFFFFFFF; +static int trigger_subsector = 0xFFFFFFFF; + // 0xFFFFFFF2 to trace all subsectors +static byte trigger_trace = 0; +#endif + +// Allocate poly from ZAlloc. +#define ZPLANALLOC + +#define POLYTILE + // -------------------------------------------------------------------------- // This is global data for planes rendering // -------------------------------------------------------------------------- -extrasubsector_t *extrasubsectors = NULL; +// ---- Polygon vertexes +// Separate allocations for level map vertexes, and BSP vertexes. -// newsubsectors are subsectors without segs, added for the plane polygons -#define NEWSUBSECTORS 50 -static size_t totsubsectors; -size_t addsubsector; +// Floating poly for level map vertexes. +// Can be indexed by level map vertex number. +polyvertex_t* poly_vert = NULL; + +// Create float poly vert from level map vertexes. +// These are freed by Z_Free( PU_HWRPLANE ). +static void create_poly_vert(void) +{ + polyvertex_t * pv; + size_t size = sizeof(polyvertex_t) * numvertexes; + size_t i; + + poly_vert = Z_Malloc(size, PU_HWRPLANE, NULL); + pv = &poly_vert[0]; + + for (i = 0; i < numvertexes; i++) + { + pv->x = FIXED_TO_FLOAT(vertexes[i].x); + pv->y = FIXED_TO_FLOAT(vertexes[i].y); + pv++; + } +} + +// Return true if p1 is a level map polyvertex in the poly_vert structure. +static inline boolean in_poly_vert(polyvertex_t * p1) +{ + return((p1 >= poly_vert) && (p1 < &poly_vert[numvertexes])); +} + +#define POLYSTORE_NUM_VERT 256 +typedef struct polyvertex_store_s { + struct polyvertex_store_s *next; // linked list + INT32 num_vert_used; + polyvertex_t pv[POLYSTORE_NUM_VERT]; +} polyvertex_store_t; + +// Extra poly vertex for BSP splits, segs, and divlines. +polyvertex_store_t *polyvert_store = NULL; + +// --- Same vertex + +// If two vertex coords have a x and y difference of less than 1 FRACUNIT, +// they could be considered the same point. +// Note: hardcoded value, 1.0f could be anything else. +//#define SAME_DIST 1.5f +// Dist 0.4999 cures HOM in Freedoom map09 +#define SAME_DIST 0.4999f +// ep : the max difference in x or y. +static inline boolean SameVertex(polyvertex_t* p1, polyvertex_t* p2, float ep) +{ + if (fabsf(p2->x - p1->x) > ep) + return false; + if (fabsf(p2->y - p1->y) > ep) + return false; + // p1 and p2 are considered the same vertex + return true; +} + +// Get a polyvertex from the BSP polyvertex store. +// These are freed by Z_Free( PU_HWRPLANE ). +static polyvertex_t *new_polyvertex(void) +{ + polyvertex_store_t *psp = polyvert_store; + + if (!polyvert_store + || (polyvert_store->num_vert_used >= POLYSTORE_NUM_VERT)) + { + // Need another storage unit. + polyvert_store = Z_Malloc(sizeof(polyvertex_store_t), PU_HWRPLANE, NULL); + polyvert_store->next = psp; // link for search + polyvert_store->num_vert_used = 0; + } + + // Return the next polyvertex in the storage unit. + return &polyvert_store->pv[polyvert_store->num_vert_used++]; +} + +// Search both the vertex lists for a close vertex. +// ep: how close they must be to be the same ( 0.001 to 1.5 ) +static polyvertex_t *find_close_polyvertex(float x, float y, float ep) +{ + polyvertex_store_t *psv; + polyvertex_t *pv; + INT32 i; + + // Search level map vertexes. + pv = poly_vert; + for (i = numvertexes; i > 0; i--) + { + const float dx = pv->x - x; + const float dy = pv->y - y; + if (dx*dx + dy*dy < ep*ep) + return pv; // close enough to be the same vertex + pv++; + } + + // Search extra BSP vertexes. + psv = polyvert_store; + while (psv) + { + // Search all vertex in a polyvertex_store_t + pv = psv->pv; + for (i = psv->num_vert_used; i > 0; i--) + { + const float dx = pv->x - x; + const float dy = pv->y - y; + if (dx*dx + dy*dy < ep*ep) + return pv; // close enough to be the same vertex + pv++; + } + psv = psv->next; + } + + return NULL; // none found +} + +// Store a new polyvertex. +// Search for an existing vertex that is within ep. +// Otherwise make a new extra vertex. +// ep: how close an existing vertex must be to be the same ( 0.001 to 1.5 ) +static inline polyvertex_t *store_polyvertex(polyvertex_t *vert, float ep) +{ + const float fx = vert->x; + const float fy = vert->y; + + polyvertex_t *vp = find_close_polyvertex(fx, fy, ep); + + if (!vp) + { + vp = new_polyvertex(); // new BSP polyvertex + vp->x = fx; + vp->y = fy; + } + + return vp; +} + +// Create a polyvertex for the vertex. +// v1 : fixed point vertex +// ep: how close an existing vertex must be to be the same ( 0.001 to 1.5 ) +static inline polyvertex_t *store_vertex(vertex_t *v1, float ep) +{ + const float fx = FIXED_TO_FLOAT(v1->x); + const float fy = FIXED_TO_FLOAT(v1->y); + polyvertex_t * vp = find_close_polyvertex(fx, fy, ep); + + if (!vp) + { + vp = new_polyvertex(); // new BSP polyvertex + vp->x = fx; + vp->y = fy; + } + + return vp; +} + +// ---- Working polygons +// Have ptr to vertex instead of copy, to make handling same vertex easier. +// Polygons are stored in clockwise vertex order. + +// Working poly +// A convex 'plane' polygon, clockwise order +typedef struct { + INT32 num_alloc, numpts; // allocation size and how many used + polyvertex_t **ppts; // ptr to array of ptrs + // Allocate with Z_Malloc, PU_HWRPLANE +#ifdef DEBUG_TRACE + UINT32 id1, id2, id3; // Tracking poly history +#endif +} wpoly_t; + +#ifdef DEBUG_TRACE +UINT32 poly_id = 1; +#endif + +#ifdef DEBUG_HWBSP +static void polyvertex_dump(polyvertex_t *pv) +{ + int j; + fixed_t x1 = pv->x * FRACUNIT; + fixed_t y1 = pv->y * FRACUNIT; + for (j = 0; j < numvertexes; j++) + { + vertex_t * vt = &vertexes[j]; + if (abs(x1 - vt->x) + abs(y1 - vt->y) < 2 ) + { + CONS_Debug(DBG_RENDER, " V%i(%6.2f, %6.2f)", j, pv->x, pv->y); + return; + } + } + + CONS_Debug(DBG_RENDER, " (%6.2f, %6.2f)", pv->x, pv->y); +} + +static void wpoly_dump(const char *str, wpoly_t *poly) +{ + int i, cnt = 0; + + cnt = strlen( str); +#ifdef DEBUG_TRACE + CONS_Debug(DBG_RENDER, " %s id=%i,%i,%i: ", str, poly->id1, poly->id2, poly->id3); + cnt += 23; +#else + CONS_Debug(DBG_RENDER, " %s: ", str); +#endif + for (i = 0; inumpts; i++) + { + if (cnt > 120 ) + { + cnt = 6; + CONS_Debug(DBG_RENDER, "\n "); + } + polyvertex_dump( poly->ppts[i]); + cnt+=20; + } + CONS_Debug(DBG_RENDER, "\n"); +} +#endif + +// Most basic initialize. +static void wpoly_init_0(wpoly_t * wpoly) +{ + wpoly->numpts = 0; + wpoly->num_alloc = 0; + wpoly->ppts = NULL; +} + +// Initialize at an initial size. +// num_alloc : num vertex, greater than 0 +static void wpoly_init_alloc(INT32 num_alloc, wpoly_t * wpoly) +{ + wpoly->numpts = 0; + wpoly->num_alloc = num_alloc; + wpoly->ppts = Z_Malloc((sizeof(void*) * num_alloc), PU_HWRPLANE, NULL); +} + +// Frees the allocation used by the wpoly. +static void wpoly_free(wpoly_t * wpoly) +{ + wpoly->num_alloc = 0; + wpoly->numpts = 0; + + if (wpoly->ppts) + { + Z_Free(wpoly->ppts); + wpoly->ppts = NULL; + } +} + +// Move all vertex from one poly to another. +// from_poly : source, is left empty +// to_poly : previous content is lost +static void wpoly_move(wpoly_t * from_poly, /*OUT*/ wpoly_t * to_poly) +{ + if (to_poly->ppts) + wpoly_free(to_poly); + + *to_poly = *from_poly; // copy ptrs and sizes + // Content moved, cannot free. + from_poly->ppts = NULL; + from_poly->num_alloc = 0; + from_poly->numpts = 0; + // from_poly is empty. +} + +// Append a range from one poly to another poly. +// Does not alloc more, will limit copy to allocation size. +static void wpoly_append(wpoly_t *src_poly, INT32 copy_from, INT32 copy_cnt, /*OUT*/ wpoly_t *dest_poly) +{ + polyvertex_t ** pvp; + INT32 n; + +#ifdef DEBUG_HWBSP + if (copy_cnt > src_poly->numpts ) + { + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds src bounds, copy_from= %i, copy_cnt= %i, src numpts= %i\n", + copy_cnt, src_poly->numpts); + } +#endif + + // Prevent writes beyond our allocation. + if (copy_cnt > dest_poly->num_alloc - dest_poly->numpts) + { +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds dest allocation, copy_cnt= %i, copy_to= %i, dest numpts= %i\n", + copy_cnt, dest_poly->numpts+1, dest_poly->numpts); +#endif + copy_cnt = dest_poly->num_alloc - dest_poly->numpts; // limit the append + } + + if (copy_cnt <= 0) + { +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, zero copy cnt, copy_cnt= %i\n", + copy_cnt); +#endif + return; + } + + pvp = & dest_poly->ppts[dest_poly->numpts]; // append + dest_poly->numpts += copy_cnt; // before copy_cnt gets decremented + + n = src_poly->numpts - copy_from; // vertexes to end of poly + if (copy_cnt > n) // copy to end of poly, then rollover + { + // Copy first portion, up to end of poly. + memcpy( pvp, &(src_poly->ppts[copy_from]), n*sizeof(void*)); + pvp += n; + // Rollover to start of src_poly + copy_cnt -= n; + copy_from = 0; + } + +#ifdef DEBUG_HWBSP + if (copy_from + copy_cnt > src_poly->numpts ) // after wrap + { + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds src bounds, copy_from= %i, copy_cnt= %i, src numpts= %i\n", + copy_from, copy_cnt, src_poly->numpts); + } +#endif +#ifdef DEBUG_HWBSP + if ((pvp + copy_cnt) > (dest_poly->ppts + dest_poly->numpts)) // after wrap + { + CONS_Debug(DBG_RENDER, "ERROR wpoly_append, exceeds dest bounds, copy_to= %i, copy_cnt= %i, numpts= %i\n", + (pvp - dest_poly->ppts), copy_cnt, dest_poly->numpts); + } +#endif + + if (copy_cnt > 0) + { + memcpy(pvp, &(src_poly->ppts[copy_from]), copy_cnt*sizeof(void*)); + } +} + + +// Insert some new vertex, and then, +// copy some of another poly to the destination poly. +// v1, v2 : polyvertex to be inserted as first vertex of poly, in this order +// src_poly : copy from src_poly +// copy_from, copy_cnt : the indexes of the vertexes to copy +// dest_poly : the destination poly +static void wpoly_split_copy(polyvertex_t * v1, polyvertex_t * v2, + wpoly_t * src_poly, INT32 copy_from, INT32 copy_cnt, + /*OUT*/ wpoly_t * dest_poly) +{ + polyvertex_t ** pvp; + INT32 n = 0; + + // Count the dest vertexes. + if (v1) + n++; + + if (v2) + n++; + + // Free old content, new allocation. + wpoly_free(dest_poly); +#ifdef DEBUG_TRACE + dest_poly->id3 = dest_poly->id2; + dest_poly->id2 = dest_poly->id1; + dest_poly->id1 = poly_id++; +#endif + wpoly_init_alloc(n + copy_cnt, dest_poly); + + // First two points of the dest_poly are the dividing seg. + dest_poly->numpts = n; // v1 and v2 + pvp = dest_poly->ppts; + + if (v1) + *pvp++ = v1; + + if (v2) + *pvp++ = v2; + + wpoly_append(src_poly, copy_from, copy_cnt, /*OUT*/ dest_poly); + } + +// Insert vertexes into the destination poly, cutout some vertexes, save some. +// v1, v2 : polyvertex to be inserted as first vertex of poly, in this order +// v_from, v_cnt : the indexes of the vertexes to save +// xpoly : the source and destination poly +static void wpoly_insert_cut(polyvertex_t *v1, polyvertex_t *v2, INT32 v_from, INT32 v_cnt, /*INOUT*/ wpoly_t *xpoly) +{ + wpoly_t tmp_poly; + + tmp_poly = *xpoly; // save ptrs and sizes + xpoly->ppts = NULL; // so does not get freed + wpoly_split_copy(v1, v2, &tmp_poly, v_from, v_cnt, /*OUT*/ xpoly); + wpoly_free(&tmp_poly); // release saved poly content +} + + +// Insert a vertex into the destination poly, at a position. +// v1 : polyvertex to be inserted +// v_at : the index where v1 is inserted +// xpoly : the source and destination poly +static void wpoly_insert_vert(polyvertex_t *v1, INT32 v_at, /*INOUT*/ wpoly_t *xpoly) +{ + wpoly_t tmp_poly; + INT32 numpts = xpoly->numpts; + + if (v_at > numpts) + return; + + tmp_poly = *xpoly; // save ptrs and sizes + // Copy back from tmp_poly to xpoly +#ifdef DEBUG_TRACE + xpoly->id3 = xpoly->id2; + xpoly->id2 = xpoly->id1; + xpoly->id1 = poly_id++; + + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, " Insert creates poly id=%i,%i,%i\n", xpoly->id1, xpoly->id2, xpoly->id3 ); + } +#endif + wpoly_init_alloc(numpts + 1, xpoly); + xpoly->numpts = numpts + 1; + + if (v_at > 0) + { + memcpy(&(xpoly->ppts[0]), &(tmp_poly.ppts[0]), sizeof(void*) * v_at); + } + + xpoly->ppts[v_at] = v1; // insert + + if (v_at < numpts) + { + memcpy(&(xpoly->ppts[v_at + 1]), &(tmp_poly.ppts[v_at]), sizeof(void*) * (numpts - v_at)); + } + + wpoly_free(&tmp_poly); // release saved poly content +} + +// ---- Subsectors + +// Array of poly_subsector_t, +// Index by bsp subsector num, 0.. num_poly_subsector-1 +poly_subsector_t *poly_subsectors = NULL; +wpoly_t *wpoly_subsectors = NULL; // working subsectors + + +// extra subsectors are subsectors without segs, added for the plane polygons +#define NUM_EXTRA_SUBSECTORS 50 +size_t num_poly_subsector = 0; +size_t num_alloc_poly_subsector = 0; + +// ========================================================================== +// FLOOR & CEILING CONVEX POLYS GENERATION +// ========================================================================== + +#ifdef DEBUG_HWBSP +//debug counters +static int nobackpoly_cnt = 0; +static int skipcut_cnt = 0; +static int total_subsecpoly_cnt = 0; +#endif + +// -------------------------------------------------------------------------- +// Polygon fast alloc / free +// -------------------------------------------------------------------------- + +#define ZPLANALLOC + +#ifndef ZPLANALLOC +#define POLY_ALLOCINC 4096 +#define POLY_VERTINC 256 +static byte* gr_polypool = NULL; +static unsigned int gr_polypool_free = 0; +#endif + +static void HWR_Freepolysubsectors(void); + +// only between levels, clear poly pool +static void HWR_ClearPolys(void) +{ + Z_FreeTags(PU_HWRPLANE, PU_HWRPLANE); +#ifndef ZPLANALLOC + gr_polypool = NULL; + gr_polypool_free = 0; +#endif + poly_vert = NULL; + polyvert_store = NULL; +} + +// allocate pool for fast alloc of polys +void HWR_InitPolyPool(void) +{ + HWR_ClearPolys(); +} + +void HWR_FreePolyPool(void) +{ + HWR_Freepolysubsectors(); + HWR_ClearPolys(); +} + +static poly_t* HWR_AllocPoly(INT32 numpts) +{ + poly_t* p; + size_t size; + + size = sizeof(poly_t) + sizeof(polyvertex_t) * numpts; +#ifdef ZPLANALLOC + p = Z_Malloc(size, PU_HWRPLANE, NULL); +#else + if (gr_polypool_free < size) + { + // Allocate another pool. + // Z_FreeTags reclaims the leftover memory of previous pool. + gr_polypool_free = POLY_ALLOCINC; + gr_polypool = Z_Malloc(gr_polypool_free, PU_HWRPLANE, NULL); + } + + p = (poly_t*) gr_polypool; + gr_polypool += size; + gr_polypool_free -= size; +#endif + p->numpts = numpts; + + return p; +} + +#ifdef DEBUG_HWBSP +// print poly for debugging +void pwpoly(wpoly_t *poly) +{ + int i; + for (i = 0; inumpts; i++) + { + if (poly->ppts[i]) + printf("(%6.2f,%6.2f)", poly->ppts[i]->x, poly->ppts[i]->y); + } + + printf("\n"); +} + +// print poly for debugging +void ppoly( poly_t * poly ) +{ + int i; + for ( i = 0; inumpts; i++ ) + printf( "(%6.2f,%6.2f)", poly->pts[i].x, poly->pts[i].y); + printf("\n"); +} +#endif + +// The BSP has the partition lines that define the subsectors. They do not +// exist anywhere else. + +// The subsectors of the BSP only have segs that are parts of linedefs. +// The subsector segs are not in any special order. +// Subsectors with 0 segs are skipped in building the BSP, so those are missing. +// Such subsectors are defined only by the dividing lines. +// The subsector of the BSP often encloses some adjoining void space. + +// Deep water in BSP: The deep water sector uses a linedef referencing a +// remote sector. The BSP will have extra dividing line polygon splits that +// are useless. The BSP builder got confused by the linedefs with remote +// sector references. +// This will result in an attempted polygon split that misses entirely. + + +// --- Divide line + +typedef enum +{ + DVL_none, // no divide + DVL_v1, // divide at v1 end of segment + DVL_mid, // divide between v1 and v2 + DVL_v2, // divide at v2 end of segment +} divline_e; typedef struct { @@ -41,138 +629,70 @@ typedef struct float dx, dy; } fdivline_t; -// ========================================================================== -// FLOOR & CEILING CONVEX POLYS GENERATION -// ========================================================================== - -//debug counters -static INT32 nobackpoly = 0; -static INT32 skipcut = 0; -static INT32 totalsubsecpolys = 0; - -// -------------------------------------------------------------------------- -// Polygon fast alloc / free -// -------------------------------------------------------------------------- -//hurdler: quick fix for those who wants to play with larger wad - -#define ZPLANALLOC -#ifndef ZPLANALLOC -//#define POLYPOOLSIZE 1024000 // may be much over what is needed -/// \todo check out how much is used -static size_t POLYPOOLSIZE = 1024000; - -static UINT8 *gl_polypool = NULL; -static UINT8 *gl_ppcurrent; -static size_t gl_ppfree; -#endif - -// only between levels, clear poly pool -static void HWR_ClearPolys(void) +#ifdef DEBUG_HWBSP +static void fdivline_dump(const char * str, fdivline_t * dl) { -#ifndef ZPLANALLOC - gl_ppcurrent = gl_polypool; - gl_ppfree = POLYPOOLSIZE; -#endif + polyvertex_t v1; + v1.x = dl->x; + v1.y = dl->y; + CONS_Debug(DBG_RENDER, "%s", str); + polyvertex_dump( & v1); + CONS_Debug(DBG_RENDER, " to ", str); + v1.x += dl->dx; + v1.y += dl->dy; + polyvertex_dump( & v1); + CONS_Debug(DBG_RENDER, " slope (%f, %f)\n", dl->dx, dl->dy); } +#endif -// allocate pool for fast alloc of polys -void HWR_InitPolyPool(void) +typedef struct { -#ifndef ZPLANALLOC - INT32 pnum; + polyvertex_t divpt; + polyvertex_t * vertex; // when same as segment endpoint + float divfrac; // how far along the partline vector is the crossing point + int before, after; // index modifiers for hitting a vertex + boolean at_vert; // crossing point is at a vertex +} div_result_t; - //hurdler: quick fix for those who wants to play with larger wad - if ((pnum = M_CheckParm("-polypoolsize"))) - POLYPOOLSIZE = atoi(myargv[pnum+1])*1024; // (in kb) - - CONS_Debug(DBG_RENDER, "HWR_InitPolyPool(): allocating %d bytes\n", POLYPOOLSIZE); - gl_polypool = malloc(POLYPOOLSIZE); - if (!gl_polypool) - I_Error("HWR_InitPolyPool(): couldn't malloc polypool\n"); - HWR_ClearPolys(); -#endif -} - -void HWR_FreePolyPool(void) +#ifdef DEBUG_HWBSP +static void divresult_dump( onst char * str, div_result_t * dr) { -#ifndef ZPLANALLOC - if (gl_polypool) - free(gl_polypool); - gl_polypool = NULL; -#endif + CONS_Debug(DBG_RENDER, "%s", str); + CONS_Debug(DBG_RENDER, " CROSS %6.4f BEFORE v1+%i AFTER v1+%i ", dr->divfrac, dr->before, dr->after); + if (dr->at_vert ) + { + CONS_Debug(DBG_RENDER, " AT"); + } + if (dr->vertex ) + { + CONS_Debug(DBG_RENDER, " SEGPT"); + polyvertex_dump( dr->vertex); + } + else + { + CONS_Debug(DBG_RENDER, " PT"); + polyvertex_dump( &dr->divpt); + } + CONS_Debug(DBG_RENDER, "\n"); } - -static poly_t *HWR_AllocPoly(INT32 numpts) -{ - poly_t *p; - size_t size = sizeof (poly_t) + sizeof (polyvertex_t) * numpts; -#ifdef ZPLANALLOC - p = Z_Malloc(size, PU_HWRPLANE, NULL); -#else -#ifdef PARANOIA - if (!gl_polypool) - I_Error("Used gl_polypool without init!\n"); - if (!gl_ppcurrent) - I_Error("gl_ppcurrent == NULL!\n"); #endif - if (gl_ppfree < size) - I_Error("HWR_AllocPoly(): no more memory %u bytes left, %u bytes needed\n\n%s\n", - gl_ppfree, size, "You can try the param -polypoolsize 2048 (or higher if needed)"); - - p = (poly_t *)gl_ppcurrent; - gl_ppcurrent += size; - gl_ppfree -= size; -#endif - p->numpts = numpts; - return p; -} - -static polyvertex_t *HWR_AllocVertex(void) -{ - polyvertex_t *p; - size_t size = sizeof (polyvertex_t); -#ifdef ZPLANALLOC - p = Z_Malloc(size, PU_HWRPLANE, NULL); -#else - if (gl_ppfree < size) - I_Error("HWR_AllocVertex(): no more memory %u bytes left, %u bytes needed\n\n%s\n", - gl_ppfree, size, "You can try the param -polypoolsize 2048 (or higher if needed)"); - - p = (polyvertex_t *)gl_ppcurrent; - gl_ppcurrent += size; - gl_ppfree -= size; -#endif - return p; -} - -/// \todo polygons should be freed in reverse order for efficiency, -/// for now don't free because it doesn't free in reverse order -static void HWR_FreePoly(poly_t *poly) -{ -#ifdef ZPLANALLOC - Z_Free(poly); -#else - const size_t size = sizeof (poly_t) + sizeof (polyvertex_t) * poly->numpts; - memset(poly, 0x00, size); - //mempoly -= polysize; -#endif -} - - -// Return interception along bsp line, +// Return interception along bsp line (partline), // with the polygon segment -// -static float bspfrac; -static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1, - polyvertex_t *v2) + +// BOOMEDIT.WAD has a vertex error of .21 +#define DIVLINE_VERTEX_DIFF 0.45f + +// partline : the dividing line +// p1, p2 : the polygon segment +// result : the result of the division +static divline_e fracdivline(fdivline_t* partline, polyvertex_t* v1, polyvertex_t* v2, + /*OUT*/ div_result_t * result) { - static polyvertex_t pt; double frac; - double num; - double den; - double v1x,v1y,v1dx,v1dy; - double v2x,v2y,v2dx,v2dy; + double den; // numerator, denominator + double v1x, v1y, v1dx, v1dy; // polygon side vector, v1->v2 + double v3x, v3y, v3dx, v3dy; // partline vector // a segment of a polygon v1x = v1->x; @@ -181,395 +701,1314 @@ static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1, v1dy = v2->y - v1->y; // the bsp partition line - v2x = bsp->x; - v2y = bsp->y; - v2dx = bsp->dx; - v2dy = bsp->dy; + v3x = partline->x; + v3y = partline->y; + v3dx = partline->dx; + v3dy = partline->dy; - den = v2dy*v1dx - v2dx*v1dy; - if (fabsf((float)den) < 1.0E-36f) // avoid checking exactly for 0.0 - return NULL; // parallel + den = v3dy*v1dx - v3dx*v1dy; + if (fabs(den) < 1.0E-36f) // avoid check of float for exact 0 + return DVL_none; // partline and polygon side are effectively parallel // first check the frac along the polygon segment, // (do not accept hit with the extensions) - num = (v2x - v1x)*v2dy + (v1y - v2y)*v2dx; - frac = num / den; - if (frac < 0.0l || frac > 1.0l) - return NULL; + const double num1 = (v3x - v1x)*v3dy + (v1y - v3y)*v3dx; + frac = num1 / den; + + // 0= cross at v1, 1.0= cross at v2 + if (frac < 0.0 || frac > 1.0) // double + return DVL_none; // not within the polygon side // now get the frac along the BSP line // which is useful to determine what is left, what is right - num = (v2x - v1x)*v1dy + (v1y - v2y)*v1dx; - frac = num / den; - bspfrac = (float)frac; + double num2 = (v3x - v1x)*v1dy + (v1y - v3y)*v1dx; + result->divfrac = num2 / den; // how far along partline vector - // find the interception point along the partition line - pt.x = (float)(v2x + v2dx*frac); - pt.y = (float)(v2y + v2dy*frac); + // [WDJ] find the interception point along the segment. + // It should be slightly more accurate because it is always closer to the + // crossing point than arbitrary positions on the partition line. + result->divpt.x = v1x + v1dx*frac; + result->divpt.y = v1y + v1dy*frac; - return &pt; + // Determine if dividing point is one of the end vertex. + // Set before and after indexes, relative to v1 index. + if (frac < 0.05 // double + && SameVertex(&result->divpt, v1, DIVLINE_VERTEX_DIFF)) + { + result->vertex = v1; + result->before = -1; // before v1 + result->after = 1; // at v2 + result->at_vert = true; + return DVL_v1; + } + + if (frac > 0.95 // double + && SameVertex(&result->divpt, v2, DIVLINE_VERTEX_DIFF)) + { + result->vertex = v2; + result->before = 0; // at v1 + result->after = 2; // after v2 + result->at_vert = true; + return DVL_v2; + } + + // Middle split + result->vertex = NULL; + result->before = 0; // at v1 + result->after = 1; // at v2 + result->at_vert = false; + return DVL_mid; } -// if two vertice coords have a x and/or y difference -// of less or equal than 1 FRACUNIT, they are considered the same -// point. Note: hardcoded value, 1.0f could be anything else. -static boolean SameVertice (polyvertex_t *p1, polyvertex_t *p2) +// Adapted from function in prboom. +// Point is to rightside of divline when result > 0, +// but result is multiplied by length of divline. +// Returns near 0, when point is on, or nearly on, the divline. +static inline float point_rightside(fdivline_t * dl, polyvertex_t * v4) { -#if 0 - float diff; - diff = p2->x - p1->x; - if (diff < -1.5f || diff > 1.5f) - return false; - diff = p2->y - p1->y; - if (diff < -1.5f || diff > 1.5f) - return false; -#elif 0 - if (p1->x != p2->x) - return false; - if (p1->y != p2->y) - return false; -#elif 0 - if (fabsf( p2->x - p1->x ) > 1.0E-36f ) - return false; - if (fabsf( p2->y - p1->y ) > 1.0E-36f ) - return false; -#else -#define DIVLINE_VERTEX_DIFF 0.45f - float ep = DIVLINE_VERTEX_DIFF; - if (fabsf( p2->x - p1->x ) > ep ) - return false; - if (fabsf( p2->y - p1->y ) > ep ) - return false; -#endif - // p1 and p2 are considered the same vertex - return true; + // Cross product of dl and vector dl->(x,y) to v4, + // is > 0 when v4 is to right side of divline. + // Viewed along divline from vertex, looking towards positive dx,dy. + // If divline is rotated until dy>0 and dx = 0, then true when rotated + // vertex position is to the right of the divline (v4->x > dl->x). + return (float) + ( (((double)(v4->x) - (double)(dl->x)) * (double)(dl->dy)) + - (((double)(v4->y) - (double)(dl->y)) * (double)(dl->dx))); } +// Return the cross product of the vector p1->p2, and p1->v4. +// The cross product is > 0 when v4 is to the right side of the vector. +// If the coordinates are rotated until the vector dy>0 and dx = 0, then the +// cross product is > 0 when v4 is to the right of the vector. +static inline double cross_product(polyvertex_t * p1, polyvertex_t * p2, polyvertex_t * v4) +{ + return + ( ((double)(v4->x) - (double)(p1->x)) * ((double)(p2->y) - (double)(p1->y)) + - ((double)(v4->y) - (double)(p1->y)) * ((double)(p2->x) - (double)(p1->x)) + ); +} -// split a _CONVEX_ polygon in two convex polygons +#ifdef POLYTILE +// Polytile list +// SplitPoly searches for the other poly with the same vertexes when it +// splits a poly segment, and adds the same vertex there too. +// This prevents any cracks from forming. + +// Hold all polygons that tile the level map. +#define POLYTILE_NUM_POLY 256 +typedef struct polytile_store_s +{ + struct polytile_store_s *next; // link for search + INT32 num_tile_used; + wpoly_t *tile[POLYTILE_NUM_POLY]; +} polytile_store_t; + +// These are freed by Z_Free( PU_HWRPLANE ). +polytile_store_t *polytile_store = NULL; +polytile_store_t *polytile_free = NULL; + +// Cleanup after usage. +static void polytile_clean(void) +{ + while (polytile_store) + { + polytile_store_t *ptp = polytile_store; + polytile_store = ptp->next; + Z_Free(ptp); + } +} + +// Save the poly ptr within the polytile lists. +static void polytile_enter(wpoly_t * poly) +{ + polytile_store_t *ptp = polytile_store; + + if (!cv_glpolytile.value) + return; + + if (poly->numpts == 0) + return; // do not enter NULL poly + + if (!polytile_store + || (polytile_store->num_tile_used >= POLYTILE_NUM_POLY)) + { + // Need another storage unit. + if (polytile_free) + { + polytile_store = polytile_free; + polytile_free = polytile_free->next; + } + else + { + polytile_store = Z_Malloc(sizeof(polytile_store_t), PU_HWRPLANE, NULL); + } + + polytile_store->next = ptp; // link for search + polytile_store->num_tile_used = 0; + } + + polytile_store->tile[ polytile_store->num_tile_used++ ] = poly; +} + +static void polytile_remove(wpoly_t * poly) +{ + polytile_store_t *ptp; + wpoly_t **wpp; + wpoly_t *lp; + INT32 i; + + // Search poly tiling. + ptp = polytile_store; + while (ptp) + { + // Search for poly in all polytile_store_t + wpp = & ptp->tile[0]; + for (i = ptp->num_tile_used-1; i >= 0; i--) + { + if (*wpp == poly) + goto found; + + wpp++; + } + ptp = ptp->next; + } + return; // not found + +found: + // Removing it gets complicated due to need to condense the list for searching. + // Move the last poly to the empty spot. There must be a last poly entered. + lp = polytile_store->tile[polytile_store->num_tile_used-1]; + *wpp = lp; // keep store compacted (ok if *wpp == lp already) + + // Remove last poly spot. + polytile_store->num_tile_used --; + if (polytile_store->num_tile_used == 0) + { + // Went empty, put on free list. + ptp = polytile_store; + polytile_store = ptp->next; + ptp->next = polytile_free; + polytile_free = ptp; + } +} + +// Seach polytile and add the new vertex between the vertex of the poly side. +// newvert : add this vertex +// poly : the poly being split +// i1, i2 : indexes of the split side +static void add_vertex_between(polyvertex_t * newvert, wpoly_t * poly, INT32 i1, INT32 i2) +{ + polytile_store_t *ptp; + wpoly_t *wp; + INT32 t, j2; + polyvertex_t *s1, *s2; + polyvertex_t *v1 = poly->ppts[i1]; + polyvertex_t *v2 = poly->ppts[i2]; + + if (!cv_glpolytile.value) + return; + +// shut up compiler +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + // Do not need to enter a vertex in vert or horz segments. + // Those do not cause problems with cracks. + if ((newvert->x == v1->x) && (newvert->x == v2->x)) + return; + + if ((newvert->y == v1->y) && (newvert->y == v2->y)) + return; +#pragma GCC diagnostic pop + + // Search poly tiling. + ptp = polytile_store; + while (ptp) + { + // Search for poly in all polytile_store_t + for (t = ptp->num_tile_used-1; t >= 0; t--) + { + wp = ptp->tile[t]; + if (wp == poly) + continue; // we already know this poly has v1,v2. + + // Search this poly for v1,v2 vertex in opposite order. + s1 = wp->ppts[wp->numpts-1]; // last vertex + for (j2 = 0; j2 < wp->numpts; j2++) + { + s2 = wp->ppts[j2]; + + if (s2 == v1) + { + if (s1 == v2) + goto found; + + break; // cannot find v1 a second time in same poly + } + + s1 = s2; + } + + } + ptp = ptp->next; + } + return; // not found + +found: +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, " Insert vertex:" ); + polyvertex_dump( newvert); + CONS_Debug(DBG_RENDER, " found in %i:", wp->id1); + polyvertex_dump( v1); + polyvertex_dump( v2); + CONS_Debug(DBG_RENDER, "\n"); + } +#endif + // Insert newvert between s1 and s2 (opposite order or v1 v2). + // (j2-1) is vertex index of s1, j2 is vertex index of s2. + wpoly_insert_vert(newvert, j2, /*INOUT*/ wp); + // Result is in the same wp. +} +#endif + +// Split a _CONVEX_ polygon in two convex polygons. +// poly : polygon to be split by divline // outputs: // frontpoly : polygon on right side of bsp line // backpoly : polygon on left side // -static void SplitPoly (fdivline_t *bsp, //splitting parametric line - poly_t *poly, //the convex poly we split - poly_t **frontpoly, //return one poly here - poly_t **backpoly) //return the other here +// Called from: HWR_WalkBSPNode +static void SplitPoly(fdivline_t *dlnp, wpoly_t *poly, /*OUT*/ wpoly_t *frontpoly, wpoly_t *backpoly) { - INT32 i,j; - polyvertex_t *pv; + // Split poly at A and B. + wpoly_t *polyA; // the poly from A to B, clockwise + wpoly_t *polyB; // the poly from B to A, clockwise + INT32 n, i, j; +#ifdef POLYTILE + INT32 A_before_wrap, B_after_wrap; +#endif + divline_e dle; + div_result_t A, B; // dividing points + div_result_t *result; - INT32 ps = -1,pe = -1; - INT32 nptfront,nptback; - polyvertex_t vs = {0,0,0}; - polyvertex_t ve = {0,0,0}; - polyvertex_t lastpv = {0,0,0}; - float fracs = 0.0f,frace = 0.0f; //used to tell which poly is on - // the front side of the bsp partition line - INT32 psonline = 0, peonline = 0; +#ifdef DEBUG_TRACE + if (trigger_trace) + { + fdivline_dump("SplitPoly: divline", dlnp); + wpoly_dump("Poly", poly); + } +#endif + if (poly->numpts < 3) + goto poly_degenerate; // not a poly, cannot split it + + result = &A; // Setup to get crossing point A for (i = 0; i < poly->numpts; i++) { - j = i + 1; - if (j == poly->numpts) j = 0; + // i, j are one side of the poly + j = i+1; + if (j == poly->numpts) + j = 0; // wrap poly - // start & end points - pv = fracdivline(bsp, &poly->pts[i], &poly->pts[j]); - - if (pv == NULL) + // Find A and B points + dle = fracdivline(dlnp, poly->ppts[i], poly->ppts[j], /*OUT*/ result); + if (dle == DVL_none) continue; - if (ps < 0) + // have dividing pt + if (result == &A) { - // first point - ps = i; - vs = *pv; - fracs = bspfrac; - } - else - { - //the partition line traverse a junction between two segments - // or the two points are so close, they can be considered as one - // thus, don't accept, since split 2 must be another vertex - if (SameVertice(pv, &lastpv)) - { - if (pe < 0) - { - ps = i; - psonline = 1; - } - else - { - pe = i; - peonline = 1; - } - } - else - { - if (pe < 0) - { - pe = i; - ve = *pv; - frace = bspfrac; - } - else - { - // a frac, not same vertice as last one - // we already got pt2 so pt 2 is not on the line, - // so we probably got back to the start point - // which is on the line - if (SameVertice(pv, &vs)) - psonline = 1; - break; - } - } + // Split at A + // Dependent upon dle, setup in fracdivline. + A.before += i; + A.after += i; + result = &B; // Setup to get crossing point B + continue; } - // remember last point intercept to detect identical points - lastpv = *pv; + // The partition line can cross at a vertex, between two segments, + // or the two points are so close, they can be considered as one. + // Crossing point B must be another vertex. + + // When ( dle == DVL_v1 || dle == DVL_v2 ) then test for same vertex. + // It is NULL for DVL_mid. + // When dividing point is at a vertex, it is found at next segment too. + if (B.vertex // ( dle == DVL_v1 || dle == DVL_v2 ) + && B.vertex == A.vertex) + continue; + + // Split at B + // Dependent upon dle, setup in fracdivline. + B.before += i; + B.after += i; // linear, no rollover + goto split_poly; // got 2 points } - // no split: the partition line is either parallel and + goto no_split; + +split_poly: +#ifdef POLYTILE + A_before_wrap = (A.before < 0)? (A.before + poly->numpts) : A.before; + B_after_wrap = (B.after >= poly->numpts)? (B.after - poly->numpts) : B.after; +#endif + + // Less aggressive same vertex, to avoid kinking line. +//#define SAME_VERTEX_DIST 0.01f +#define SAME_VERTEX_DIST 0.08f + if (A.vertex == NULL) + { + A.vertex = store_polyvertex(&A.divpt, SAME_VERTEX_DIST); +#ifdef POLYTILE + add_vertex_between(A.vertex, poly, A_before_wrap, A.after); +#endif + } + + if (B.vertex == NULL) + { + B.vertex = store_polyvertex(&B.divpt, SAME_VERTEX_DIST); +#ifdef POLYTILE + add_vertex_between(B.vertex, poly, B.before, B_after_wrap); +#endif + } + + // The frontpoly is the one on the 'right' side + // of the partition line. + if (A.divfrac > B.divfrac) + { + polyA = frontpoly; + polyB = backpoly; + } + else + { + polyA = backpoly; + polyB = frontpoly; + } + + // Form PolyA + // Number of points from A to B clockwise. + n = B.before - A.after + 1; + if (n > 0) + { + // B, A, poly from A to B clockwise + wpoly_split_copy(B.vertex, A.vertex, poly, A.after, n, /*OUT*/ polyA); + } + else + polyA->numpts = 0; + + // Form PolyB + // Number of points from B to A clockwise. + n = A.before + poly->numpts - B.after + 1; + if (n > 0) + { + // A, B, poly from B to A clockwise + wpoly_split_copy(A.vertex, B.vertex, poly, + ((B.after < poly->numpts)? B.after : (B.after - poly->numpts)), + n, /*OUT*/ polyB); + } + else + polyB->numpts = 0; + +#ifdef DEBUG_HWBSP + // Test that frontpoly is to the right + if (frontpoly->numpts >= 2 + && (point_rightside( dlnp, frontpoly->ppts[2] ) < -0.5f )) + CONS_Printf( EMSG_warn, "SplitPoly: frontpoly on left side\n"); + + // Test that backpoly is to the left + if (backpoly->numpts >= 2 + && (point_rightside( dlnp, backpoly->ppts[2] ) > 0.5f )) + CONS_Printf( EMSG_warn, "SplitPoly: backpoly on right side\n"); +#endif +#ifdef DEBUG_TRACE + frontpoly->id3 = poly->id2; + frontpoly->id2 = poly->id1; + backpoly->id3 = poly->id2; + backpoly->id2 = poly->id1; + + if (trigger_trace) + { + wpoly_dump( " Front Poly", frontpoly); + wpoly_dump( " Back Poly", backpoly); + } +#endif + + return; + +no_split: + // no split : the partition line is either parallel and // aligned with one of the poly segments, or the line is totally // out of the polygon and doesn't traverse it (happens if the bsp // is fooled by some trick where the sidedefs don't point to // the right sectors) - if (ps < 0) + if (result == &A) { - //I_Error("SplitPoly: did not split polygon (%d %d)\n" - // "debugpos %d",ps,pe,debugpos); +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "DEBUG: SplitPoly: divline missed entirely\n"); +#endif +#ifdef DEBUG_TRACE + // no results to dump +#endif // this eventually happens with 'broken' BSP's that accept // linedefs where each side point the same sector, that is: // the deep water effect with the original Doom - - /// \todo make sure front poly is to front of partition line? - - *frontpoly = poly; - *backpoly = NULL; - return; } - - if (pe < 0) + else if (A.vertex == NULL) { - //I_Error("SplitPoly: only one point for split line (%d %d)", ps, pe); - *frontpoly = poly; - *backpoly = NULL; - return; - } - if (pe <= ps) - I_Error("SplitPoly: invalid splitting line (%d %d)", ps, pe); - - // number of points on each side, _not_ counting those - // that may lie just one the line - nptback = pe - ps - peonline; - nptfront = poly->numpts - peonline - psonline - nptback; - - if (nptback > 0) - *backpoly = HWR_AllocPoly(2 + nptback); - else - *backpoly = NULL; - if (nptfront > 0) - *frontpoly = HWR_AllocPoly(2 + nptfront); - else - *frontpoly = NULL; - - // generate FRONT poly - if (*frontpoly) - { - pv = (*frontpoly)->pts; - *pv++ = vs; - *pv++ = ve; - i = pe; - do +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, + "DEBUG: SplitPoly: one new divide point %d (%6.2f,%6.2f) %d\n", + A.before, A.divpt.x, A.divpt.y, A.after); +#endif +#ifdef DEBUG_TRACE + if (trigger_trace) { - if (++i == poly->numpts) - i = 0; - *pv++ = poly->pts[i]; - } while (i != ps && --nptfront); + // Found a crossing A point, but did not find a B crossing. Should not happen. + divresult_dump( " CROSSING-A", &A); + } +#endif } - - // generate BACK poly - if (*backpoly) + else { - pv = (*backpoly)->pts; - *pv++ = ve; - *pv++ = vs; - i = ps; - do +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, + "DEBUG: SplitPoly: intersect at one vertex, %d\n", + A.before + 1); +#endif +#ifdef DEBUG_TRACE + if (trigger_trace) { - if (++i == poly->numpts) - i = 0; - *pv++ = poly->pts[i]; - } while (i != pe && --nptback); + // Found A point at vertex, but did not find a B crossing. Just touched a vertex. + divresult_dump( " CROSSING-A", &A); + } +#endif } - // make sure frontpoly is the one on the 'right' side - // of the partition line - if (fracs > frace) + // [WDJ] The front and back MUST match the BSP determination, else it will assign + // this poly to the wrong sector. + // May have 1 point on the line, and other points might be very close to the line. + // Can only trust a test of a point that is NOT on the line. + // Using a tstdist = 0.5f worked, but it might break on very small poly. + // Avactor.wad had polys with d = 9.09, 5552, 1.64, 1011, 14252, 0.0136, 0.00634, 14655, 3, + // -0.3044, 7, 1.53, etc.., + // and one poly with d = -0.023 with the rest of the points d > 0. + // One 14 point poly with divline len=8, had to test 9 points to find d = 36. + // Typical: divline len=70 d=6828, divline len=1.0 d=35, divline len=1.414 d=5. + // The result of point_rightside is proportional to the length of divline. + // The divline length can be from 1.414 to 14000. + const float tstd = (sqrtf((dlnp->dx * dlnp->dx) + (dlnp->dy * dlnp->dy))) / 2.0f; + float sumd = 0; // accumulated left or rightness. + + for (i = 0; i < poly->numpts; i++) { - poly_t *swappoly; - swappoly = *backpoly; - *backpoly = *frontpoly; - *frontpoly = swappoly; + // Look for a point that is obviously to the left or right. + float d = point_rightside(dlnp, poly->ppts[i]); // d > 0 is right + sumd += d; + + if (d > tstd) + goto poly_rightside; + + if (d < -tstd) + goto poly_leftside; } - HWR_FreePoly (poly); + // Did not find an obvious left or right point, then + if (sumd < 0) + goto poly_leftside; + +poly_rightside: + // poly is to rightside of divline. + wpoly_move(poly, frontpoly); + backpoly->numpts = 0; + return; + +poly_leftside: + // poly is to leftside of divline. + wpoly_move(poly, backpoly); + frontpoly->numpts = 0; + return; + +poly_degenerate: + // This cannot lead to any subsector polys. +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "DEBUG: SplitPoly: degenerate poly has %d points\n", poly->numpts); +#endif + frontpoly->numpts = 0; + backpoly->numpts = 0; + return; +} + +// The BSP creates convex polygons, up to the point where it presents +// the subsector. The subsector segs may adjoin void space, and if that +// is cut out of the subsector polygon it might not be convex. +// Where the cutting seg does not entirely traverse the polygon, +// it should only cut over its length, not entirely to the other side +// of the polygon. Another seg, and maybe more, must be present to +// finish the cut to the other side of the polygon. There is no +// assurance that these segs will keep the polygon convex. +// Example: BOOMEDIT.WAD, subsector 206 is not convex after cutting segs. +// It is cut by seg linedef 104, and seg linedef 110. + +#define CUTOUT_NON_CONVEX 1 + +#ifdef CUTOUT_NON_CONVEX +// Force convex solution. +static void enforce_convex(wpoly_t * poly) +{ + polyvertex_t *rv1, *rv2, *rv3; + INT32 i1, i2, i3; + INT32 numpts = poly->numpts; + + for (i2 = 0; i2 < numpts;) + { + // Check that angle at i2 is less than 180. + i3 = i2 + 1; + if (i3 == numpts) + i3 = 0; + + rv3 = poly->ppts[i3]; + + i1 = i2 - 1; + if (i1 < 0) + i1 += numpts; + + rv1 = poly->ppts[i1]; + rv2 = poly->ppts[i2]; + + // cross product to check angle + if (cross_product( rv1, rv2, rv3 ) < 0) + { + // Remove the i2 vertex, to make the polygon more convex. + if ((i2+1) < numpts) + { // Shuffle down by 1 + memmove(&poly->ppts[i2], &poly->ppts[i2+1], sizeof(void*)*(numpts - (i2+1))); + } + numpts--; + + if (numpts <= 3) + break; + + // Have to repeat from i1 vertex, because that vertex angle + // changed too. + i2 = (i1 > 0) ? i1 : 0; + continue; + } + + i2++; + } + + poly->numpts = numpts; +} +#endif + +#ifdef CUTOUT_NON_CONVEX +// Some of the segs will only partially cross the polygon, but connect with +// another seg, and together will cross it, or form a corner. +// Trying to cut with these leads to an incomplete or non-convex polygon. +// Then latter seg cuts will have 3 or 4 crossings. +// Deal with linking the segs together separate from the seg cutting operation. + +typedef struct loose_seg_s +{ + struct loose_seg_s *next; + seg_t *seg; // the seg + polyvertex_t *p1, *p2; // clockwise order +} loose_seg_t; + +typedef struct seg_chain_s +{ + struct seg_chain_s *next; + loose_seg_t *first_seg; // head of seg-chain + loose_seg_t *last_seg; // tail of seg-chain + polyvertex_t *p1, *p2; // ends in clockwise order + boolean loose1, loose2; // loose ends of the seg-chain + INT32 num_seg; +} seg_chain_t; + +// Nothing to gain by making this a parameter, this saves param passing. +// Easier to deal with releasing memory. +static seg_chain_t *seg_chain = NULL; // Z_Malloc + +static void free_first_seg_chain(void) +{ + seg_chain_t *sctp; + loose_seg_t *lsp, *lsp2; + + sctp = seg_chain; + + if (sctp) + { + seg_chain = sctp->next; + // Free the list of loose segs + lsp = sctp->first_seg; + + while (lsp) + { + lsp2 = lsp->next; + Z_Free(lsp); + lsp = lsp2; + } + + Z_Free(sctp); + } +} + +// Clear out the seg_chain structure +static void clear_seg_chains(void) +{ + while (seg_chain) + free_first_seg_chain(); +} + +// The seg-chains may be in portions, due to entry order. +static void condense_seg_chains(void) +{ + seg_chain_t *sctp; + seg_chain_t *sctp2; + seg_chain_t *sctp2_prev; + polyvertex_t *pv2; + + // Search seg-chains for combinations. + sctp = seg_chain; + while (sctp) + { + if (sctp->loose2) // p2 is loose + { + // Search for pv2 as loose first in another seg-chain + pv2 = sctp->p2; + sctp2_prev = NULL; + sctp2 = seg_chain; + + while(sctp2) + { + if (sctp2 != sctp + && sctp2->loose1 + && sctp2->p1 == pv2) + goto combine; + + sctp2_prev = sctp2; + sctp2 = sctp2->next; + } + } + + sctp = sctp->next; + + continue; + +combine: + // Append sctp2 to last of sctp seg-chain. + sctp->last_seg->next = sctp2->first_seg; + sctp->last_seg = sctp2->last_seg; + sctp->p2 = sctp2->p2; + sctp->loose2 = sctp2->loose2; + sctp->num_seg += sctp2->num_seg; + + // Remove sctp2 + if (sctp2_prev) + sctp2_prev->next = sctp2->next; + else + seg_chain = sctp2->next; + + Z_Free( sctp2); + + continue; // with same sctp + } +} + +// Save loose segs in the seg_chain structure. +// B_A_order : the vertex order is B A for clockwise +static void save_loose_seg(seg_t *loose_seg, + polyvertex_t *vA, polyvertex_t *vB, + boolean looseA, boolean looseB, + boolean B_A_order) +{ + loose_seg_t *lsp; + seg_chain_t *sctp; + polyvertex_t *pv1, *pv2; + boolean loose1, loose2; + + // B_A_order is determined by the order of the polygon sides. + if (B_A_order) + { + pv1 = vB; + pv2 = vA; + loose1 = looseB; + loose2 = looseA; + } + else + { + pv1 = vA; + pv2 = vB; + loose1 = looseA; + loose2 = looseB; + } + + lsp = Z_Malloc(sizeof(loose_seg_t), PU_HWRPLANE, NULL); + lsp->seg = loose_seg; + lsp->p1 = pv1; + lsp->p2 = pv2; + lsp->next = NULL; + + // Search seg-chains + sctp = seg_chain; + while (sctp) + { + if (loose2 + && sctp->loose1 + && sctp->p1 == pv2) + { + // first of seg-chain + lsp->next = sctp->first_seg; + sctp->first_seg = lsp; + sctp->p1 = pv1; + sctp->loose1 = loose1; + sctp->num_seg++; + return; + } + + if (loose1 + && sctp->loose2 + && sctp->p2 == pv1 ) + { + // last of seg-chain + lsp->next = NULL; + sctp->last_seg->next = lsp; + sctp->last_seg = lsp; + sctp->p2 = pv2; + sctp->loose2 = loose2; + sctp->num_seg++; + return; + } + + sctp = sctp->next; + } + + // Need new seg-chain + sctp = Z_Malloc(sizeof(seg_chain_t), PU_HWRPLANE, NULL); + sctp->next = NULL; + sctp->num_seg = 1; + sctp->first_seg = lsp; + sctp->last_seg = lsp; + sctp->p1 = pv1; + sctp->p2 = pv2; + sctp->loose1 = loose1; + sctp->loose2 = loose2; + sctp->next = seg_chain; + seg_chain = sctp; } -// use each seg of the poly as a partition line, keep only the -// part of the convex poly to the front of the seg (that is, -// the part inside the sector), the part behind the seg, is -// the void space and is cut out -// -static poly_t *CutOutSubsecPoly(seg_t *lseg, INT32 count, poly_t *poly) +//#define SEG_CHAIN_2 + +// Apply the list of loose end seg chains. +// Return true if a possible non-convex cut is made. +static boolean apply_seg_chains(wpoly_t *poly) { - INT32 i, j; + boolean check_convex = false; + wpoly_t comb_poly; // combine seg-chain and poly + loose_seg_t *lsp; + seg_chain_t *sctp; + polyvertex_t *rv1, *rv2; + INT32 n, i1, i2; + INT32 numpts = poly->numpts; - polyvertex_t *pv; - - INT32 nump = 0, ps, pe; - polyvertex_t vs = {0, 0, 0}, ve = {0, 0, 0}, - p1 = {0, 0, 0}, p2 = {0, 0, 0}; - float fracs = 0.0f; - - fdivline_t cutseg; // x, y, dx, dy as start of node_t struct - - poly_t *temppoly; - - // for each seg of the subsector - for (; count--; lseg++) + // All the seg-chains are part of the polygon. If a seg-chain is + // outside the polygon, the polygon must be expanded to include it. + // Otherwise there will be cracks in the floor. + for (;;) { - line_t *line = lseg->linedef; + sctp = seg_chain; + if (!sctp) + break; - if (lseg->glseg) + rv1 = sctp->p1; + rv2 = sctp->p2; + i1 = i2 = numpts + 20; // invalid + +#ifndef SEG_CHAIN_2 + if (sctp->loose1 || sctp->loose2) + { + // FIXME: This needs to be handled. + goto reject; + } +#endif + +#ifdef SEG_CHAIN_2 + if (! sctp->loose1 ) +#endif + { + // Find original crossing point. + for ( i1 = 0; i1 < numpts; i1++) + { + if (poly->ppts[i1] == rv1) + break; + } + } + +#ifdef SEG_CHAIN_2 + if (! sctp->loose2 ) +#endif + { + // Find original crossing point. + for (i2 = 0; i2 < numpts; i2++) + { + if (poly->ppts[i2] == rv2) + break; + } + } + +#ifdef SEG_CHAIN_2 + if (sctp->loose1 && ! sctp->loose2 ) + { + } + + if (sctp->loose2 && ! sctp->loose1 ) + { + } + + if (sctp->loose1 && sctp->loose2 ) + { + } +#endif + + if ((i1 < numpts) && (i2 < numpts)) + { + // Insert points are still there. + // Alloc the right size comb_poly. + i2 ++; // copy after i2 + if (i2 >= numpts) + i2 -= numpts; + + n = i1 - i2; // not inclusive of i1 or i2 + if (n < 0) + n += numpts; + + wpoly_init_alloc(sctp->num_seg + 1 + n, & comb_poly); +#ifdef DEBUG_TRACE + comb_poly.id3 = poly->id2; + comb_poly.id2 = poly->id1; + comb_poly.id1 = poly_id++; +#endif + // Copy the seg-chain into the comb_poly. + polyvertex_t ** ppv = comb_poly.ppts; + + for (lsp = sctp->first_seg; lsp; lsp = lsp->next) + { + *ppv++ = lsp->p1; // rv1 to before rv2 + } + + *ppv = rv2; + comb_poly.numpts = sctp->num_seg + 1; + + // It is valid for n == 0, with num_seg > 1, + // and both end points on the same poly side. + if (n > 0) + { + // Save i2 to i1, which starts after rv2, to the comb_poly. + wpoly_append(poly, i2, n, /*OUT*/ & comb_poly); + } + + wpoly_move(&comb_poly, /*OUT*/ poly); // empty comb_poly + numpts = poly->numpts; + check_convex = true; + } +reject: + free_first_seg_chain(); + } + + return check_convex; +} + +#endif + +// Use each seg of the poly as a partition line, keep only the +// part of the convex poly to the front of the seg (that is, +// the part inside the sector). The part behind the seg, is +// the void space and is cut out. +// +// poly : surrounding convex polygon, non-destructive +// ssindex : subsec index, 0..(numsubsectors-1) +// Called from: HWR_SubsecPoly +static void CutOutSubsecPoly(INT32 ssindex, /*INOUT*/ wpoly_t* poly) +{ + subsector_t* sub; + seg_t *lseg; // array of seg + INT16 segcount; // number of seg in the array + + polyvertex_t *rv1, *rv2; // A,B or B,A + boolean cut_at_vert; + +#ifdef CUTOUT_NON_CONVEX + boolean check_convex = false; + boolean looseA, looseB; +#endif + + vertex_t *v1, *v2; + polyvertex_t p1, p2; + fdivline_t cutseg; // x,y,dx,dy as start of node_t struct + divline_e dle; + + div_result_t A, B; // dividing points + div_result_t *result; + INT32 poly_num_pts, ps, n; + INT32 i1, i2; + + poly_num_pts = poly->numpts; + sub = &subsectors[ssindex]; + segcount = sub->numlines; + lseg = &segs[sub->firstline]; + + // For each seg of the subsector + for (; segcount--; lseg++) + { + //x,y,dx,dy (like a divline) + const line_t *line = lseg->linedef; + + if (!line || lseg->glseg) continue; - //x,y,dx,dy (like a divline) - p1.x = FIXED_TO_FLOAT(lseg->side ? line->v2->x : line->v1->x); - p1.y = FIXED_TO_FLOAT(lseg->side ? line->v2->y : line->v1->y); - p2.x = FIXED_TO_FLOAT(lseg->side ? line->v1->x : line->v2->x); - p2.y = FIXED_TO_FLOAT(lseg->side ? line->v1->y : line->v2->y); + if (line->sidenum[1] != 0xffff) + { + if (sides[line->sidenum[0]].sector == sides[line->sidenum[1]].sector) + { + // Segs that are self-ref linedef do not cutout the subsector. +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "CutOutSubsecPoly: self ref line %i\n", line - lines); +#endif + continue; + } + } + + if (lseg->side) + { // side 1 + v1 = line->v2; + v2 = line->v1; + } + else + { // side 0 + v1 = line->v1; + v2 = line->v2; + } + + p1.x = FIXED_TO_FLOAT(v1->x); + p1.y = FIXED_TO_FLOAT(v1->y); + p2.x = FIXED_TO_FLOAT(v2->x); + p2.y = FIXED_TO_FLOAT(v2->y); cutseg.x = p1.x; cutseg.y = p1.y; cutseg.dx = p2.x - p1.x; cutseg.dy = p2.y - p1.y; - // see if it cuts the convex poly - ps = -1; - pe = -1; - for (i = 0; i < poly->numpts; i++) + // See if it cuts the convex poly + result = &A; // Setup to get crossing point A + + for (i1 = 0; i1 < poly_num_pts; i1++) { - j = i + 1; - if (j == poly->numpts) - j = 0; + i2 = i1 + 1; + if (i2 >= poly_num_pts) + i2 = 0; - pv = fracdivline(&cutseg, &poly->pts[i], &poly->pts[j]); + // i1, i2 are one side of the poly + dle = fracdivline(&cutseg, poly->ppts[i1], poly->ppts[i2], /*OUT*/ result); - if (pv == NULL) + if (dle == DVL_none) continue; - if (ps < 0) + // have dividing pt + if (result == &A) { - ps = i; - vs = *pv; - fracs = bspfrac; + // Split at A + // Dependent upon dle, setup in fracdivline. + A.before += i1; + A.after += i1; + result = &B; // Setup to get crossing point B + continue; } - else - { - //frac 1 on previous segment, - // 0 on the next, - //the split line goes through one of the convex poly - // vertices, happens quite often since the convex - // poly is already adjacent to the subsector segs - // on most borders - if (SameVertice(pv, &vs)) - continue; - if (fracs <= bspfrac) - { - nump = 2 + poly->numpts - (i-ps); - pe = ps; - ps = i; - ve = *pv; - } - else - { - nump = 2 + (i-ps); - pe = i; - ve = vs; - vs = *pv; - } - //found 2nd point - break; - } + // The partition line can cross at a vertex, between two segments, + // or the two points are so close, they can be considered as one. + // Crossing point B must be another vertex. + + // When ( dle == DVL_v1 || dle == DVL_v2 ) then test for + // the same vertex. It is NULL for DVL_mid. + // When dividing point is at a vertex, is found at next segment too. + if (B.vertex // ( dle == DVL_v1 || dle == DVL_v2 ) + && B.vertex == A.vertex ) continue; + + // Split at B + // Dependent upon dle, setup in fracdivline. + B.before += i1; + B.after += i1; // linear, no rollover + goto cut_poly; // got 2 points } - // there was a split - if (ps >= 0) + // need 2 points + if (result == &B) { - //need 2 points - if (pe >= 0) - { - // generate FRONT poly - temppoly = HWR_AllocPoly(nump); - pv = temppoly->pts; - *pv++ = vs; - *pv++ = ve; - do - { - if (++ps == poly->numpts) - ps = 0; - *pv++ = poly->pts[ps]; - } while (ps != pe); - HWR_FreePoly(poly); - poly = temppoly; - } //hmmm... maybe we should NOT accept this, but this happens // only when the cut is not needed it seems (when the cut // line is aligned to one of the borders of the poly, and // only some times..) - else - skipcut++; - // I_Error("CutOutPoly: only one point for split line (%d %d) %d", ps, pe, debugpos); + // [WDJ] This happens often (27 times in Doom2 map01). + +#ifdef DEBUG_HWBSP + skipcut_cnt++; + //CONS_Debug(DBG_RENDER, + //"DEBUG: CutOutPoly: only one point for split line (%d %d)\n",ps,pe); +#endif + // It must have crossed at one polygon vertex. + // That is the only way to touch at only one point. + // The seg is usually slightly misaligned with the polygon side, + // leaving a crack in front of a wall. + // Let the seg-chains take care of it. It is important to + // extend the poly to all seg walls to eliminate cracks. + if (A.vertex == NULL) + { + A.vertex = store_polyvertex(&A.divpt, 0.25f); + } + + // Store the other vertex + B.vertex = store_polyvertex(((A.divfrac > 0.5) ? &p1 : &p2), 0.25f); + + // Get another point in the poly to rv1 + i2 = A.after; + + if (i2 >= poly_num_pts) + i2 = 0; + + rv1 = poly->ppts[i2]; + save_loose_seg(lseg, A.vertex, B.vertex, false, true, + cross_product(A.vertex, B.vertex, rv1) < 0); + + continue; } + continue; // no split by this segment + +cut_poly: + // Cuts that miss the polygon. + if ((A.divfrac < 0.0) && (B.divfrac < 0.0)) + continue; + + if ((A.divfrac > 1.0) && (B.divfrac > 1.0)) + continue; + + // Skip cuts that duplicate an existing side. + // Does not matter if goes across poly or not. + cut_at_vert = A.at_vert && B.at_vert; // cuts are at existing vertexes + + if (cut_at_vert) + { + // Skip if both crossings are on same polygon segment. + if (A.after + 1 == B.after) + continue; + + if (A.after + poly_num_pts == B.after + 1) + continue; + } + +#ifdef CUTOUT_NON_CONVEX +#define FRAC_EP 0.01f + // Cuts that may make the polygon non-convex. + looseA = (A.divfrac < (0.0 - FRAC_EP)) || (A.divfrac > (1.0f + FRAC_EP)); + + if (looseA) + { + // Cannot make normal cut, A end is loose. + if (cv_glpolyshape.value == 1) + continue; // fat polygons + + // Get vertex at A end of seg, v1 or v2 + A.vertex = store_polyvertex(((A.divfrac < 0.0) ? &p1 : &p2), 0.25f); + } + + looseB = (B.divfrac < (0.0 - FRAC_EP)) || (B.divfrac > (1.0f + FRAC_EP)); + + if (looseB) + { + // Cannot make normal cut, B end is loose. + if (cv_glpolyshape.value == 1) + continue; // fat polygons + + // Get vertex at B end of seg, v1 or v2 + B.vertex = store_polyvertex(((B.divfrac < 0.0 ) ? &p1 : &p2), 0.25f); + + if (looseA) // from A end + { + // Both ends are loose. + // All that can be done is to save the seg + save_loose_seg(lseg, A.vertex, B.vertex, true, true, + (B.divfrac < A.divfrac )); + continue; + } + } +#else + // Reject cuts that could make the polygon non-convex. + if ((A.divfrac < 0.0) || (A.divfrac > 1.0)) + continue; + + if ((B.divfrac < 0.0) || (B.divfrac > 1.0)) + continue; +#endif + + // Store new crossing vertex. + if (A.vertex == NULL) + { + A.vertex = store_polyvertex(&A.divpt, 0.25f); + } + + if (B.vertex == NULL) + { + B.vertex = store_polyvertex(&B.divpt, 0.25f); + } + +#ifdef CUTOUT_NON_CONVEX + if (looseA || looseB) + { + // Cutseg does not completely cross the poly. + // Save the seg + save_loose_seg(lseg, A.vertex, B.vertex, looseA, looseB, + (B.divfrac < A.divfrac)); + + // Insert the crossing vertex into the poly, as a marker. + // Must occur after the crossing vertex A, B are stored. + if (looseA) + { + // A is loose, B is crossing point. + if (B.at_vert) + continue; // Point is at existing vertex + + rv1 = B.vertex; + ps = B.before + 1; // insert point + } + else + { + // B is loose, A is crossing point. + if (A.at_vert) + continue; // Point is at existing vertex + + rv1 = A.vertex; + ps = A.before + 1; // insert point + } + rv2 = NULL; + n = poly_num_pts; + } + else +#endif + { + // Cutseg cuts across poly, at two points. + // Save poly on rightside of cutting seg. + if (B.divfrac < A.divfrac) + { + // B, A, poly from A to B clockwise + // Number of points from A to B clockwise. + n = B.before - A.after + 1; + ps = A.after; + rv1 = B.vertex; + rv2 = A.vertex; + } + else + { + // A, B, poly from B to A clockwise + // Number of points from B to A clockwise. + n = A.before + poly_num_pts - B.after + 1; + ps = B.after; + rv1 = A.vertex; + rv2 = B.vertex; + } + + // If cut at existing vertexes, and it has the same number of pts, + // then the cut is already an existing side, + // and the polygon is not going to change. + if (cut_at_vert && ((n+2) == poly_num_pts)) + continue; + } + + if (ps >= poly_num_pts) + ps -= poly_num_pts; + + wpoly_insert_cut( rv1, rv2, ps, n, poly); + poly_num_pts = poly->numpts; } - return poly; + +#ifdef CUTOUT_NON_CONVEX + // After all normal seg cuts, deal with the loose end segs. + if (seg_chain) + { + // Have some loose end segs. + condense_seg_chains(); + check_convex = apply_seg_chains( poly); + clear_seg_chains(); + } + + if (check_convex + && (cv_glpolyshape.value == 2) // Trim polygons + && (poly_num_pts > 3)) + { + // Need to check convex. + // Force convex solution, or split into convex. + enforce_convex(poly); + } +#endif } + // At this point, the poly should be convex and the exact -// layout of the subsector, it is not always the case, +// layout of the subsector. It is not always the case, // so continue to cut off the poly into smaller parts with // each seg of the subsector. // -static inline void HWR_SubsecPoly(INT32 num, poly_t *poly) +// ssindex : subsec index, 0..(numsubsectors-1) +// poly : surrounding convex polygon, non-destructive +// Called from HWR_WalkBSPNode +static void HWR_SubsecPoly(int ssindex, wpoly_t* poly) { - INT16 count; - subsector_t *sub; - seg_t *lseg; - - sub = &subsectors[num]; - count = sub->numlines; - lseg = &segs[sub->firstline]; - - if (poly) +#ifdef DEBUG_HWBSP +#ifdef DEBUG_TRACE { - poly = CutOutSubsecPoly (lseg,count,poly); - totalsubsecpolys++; - //extra data for this subsector - extrasubsectors[num].planepoly = poly; + subsector_t* sub = &subsectors[ssindex]; + + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "HWR_SubsecPoly: sector %i, subsector %i, poly->numpts= %i\n", sub->sector - sectors, ssindex, poly->numpts); + } } +#else + if (poly->numpts <= 0) + { + subsector_t* sub = &subsectors[ssindex]; + CONS_Debug(DBG_RENDER, "HWR_SubsecPoly: sector %i, subsector %i, poly->numpts= %i\n", sub->sector - sectors, ssindex, poly->numpts); + } +#endif +#endif + if (poly->numpts <= 0) + return; + + if (cv_glpolyshape.value > 0) + { + // Trim the subsector with the segs. + CutOutSubsecPoly(ssindex, /*INOUT*/ poly); + } + +#ifdef DEBUG_TRACE + if (trigger_trace) + { + wpoly_dump("Subsec poly", poly); + } +#endif +#ifdef DEBUG_HWBSP + total_subsecpoly_cnt++; +#endif } -// the bsp divline have not enouth presition -// search for the segs source of this divline -static inline void SearchDivline(node_t *bsp, fdivline_t *divline) +// The bsp divline does not have enough precision. +// Search for the segs source of this divline. +static inline void set_divline(node_t* bsp, fdivline_t *divline) { - divline->x = FIXED_TO_FLOAT(bsp->x); - divline->y = FIXED_TO_FLOAT(bsp->y); + divline->x = FIXED_TO_FLOAT(bsp->x); + divline->y = FIXED_TO_FLOAT(bsp->y); divline->dx = FIXED_TO_FLOAT(bsp->dx); divline->dy = FIXED_TO_FLOAT(bsp->dy); } @@ -605,422 +2044,930 @@ static void loading_status(void) #endif // poly : the convex polygon that encloses all child subsectors -static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *bbox) +// Recursive +// bspnum : children[] +// leafnode : & children [] +// Call HWR_WalkBSPNode_degen for degenerate case. +// Called from HWR_CreatePlanePolygons at load time. +static void HWR_WalkBSPNode(INT32 bspnum, wpoly_t* poly, UINT16 *leafnode, fixed_t *bbox) { - node_t *bsp; - poly_t *backpoly, *frontpoly; + node_t* bsp; + wpoly_t backpoly; + wpoly_t frontpoly; fdivline_t fdivline; polyvertex_t *pt; + INT32 subsecnum; // subsector index INT32 i; + (void)leafnode; // unused + +#ifdef DEBUG_TRACE + if (trigger_bsp_sector == 0xFFFFFFF2 ) + { + trigger_trace = 2; // trace all + } +#endif + // Found a subsector? if (bspnum & NF_SUBSECTOR) { - if (bspnum == -1) + // Subsector leaf: bspnum is a subsector number. + subsecnum = bspnum & ~NF_SUBSECTOR; + if ((size_t)subsecnum >= numsubsectors) + goto bad_subsector; + +#ifdef DEBUG_TRACE + if (trigger_bsp_sector < numsectors + && subsectors[subsecnum].sector == & sectors[trigger_bsp_sector] ) { - // BP: i think this code is useless and wrong because - // - bspnum==-1 happens only when numsubsectors == 0 - // - it can't happens in bsp recursive call since bspnum is a INT32 and children is UINT16 - // - the BSP is complet !! (there just can have subsector without segs) (i am not sure of this point) - - // do we have a valid polygon ? - if (poly && poly->numpts > 2) - { - CONS_Debug(DBG_RENDER, "Adding a new subsector\n"); - if (addsubsector == numsubsectors + NEWSUBSECTORS) - I_Error("WalkBSPNode: not enough addsubsectors\n"); - else if (addsubsector > 0x7fff) - I_Error("WalkBSPNode: addsubsector > 0x7fff\n"); - *leafnode = (UINT16)((UINT16)addsubsector | NF_SUBSECTOR); - extrasubsectors[addsubsector].planepoly = poly; - addsubsector++; - } - - //add subsectors without segs here? - //HWR_SubsecPoly(0, NULL); + CONS_Debug(DBG_RENDER, "BSP TRIGGER SECTOR %i: \n", trigger_bsp_sector); + trigger_trace = 1; } - else - { - HWR_SubsecPoly(bspnum & ~NF_SUBSECTOR, poly); - - //Hurdler: implement a loading status -#ifdef HWR_LOADING_SCREEN - if (ls_count-- <= 0) - { - ls_count = numsubsectors/50; - loading_status(); - } #endif - } + + HWR_SubsecPoly(subsecnum, poly); + + // Add the poly points into the bounding box. M_ClearBox(bbox); - poly = extrasubsectors[bspnum & ~NF_SUBSECTOR].planepoly; + for (i = 0; i < poly->numpts; i++) + { + pt = poly->ppts[i]; + M_AddToBox(bbox, (fixed_t)(pt->x * FRACUNIT), (fixed_t)(pt->y * FRACUNIT)); + } - for (i = 0, pt = poly->pts; i < poly->numpts; i++,pt++) - M_AddToBox(bbox, FLOAT_TO_FIXED(pt->x), FLOAT_TO_FIXED(pt->y)); +#ifdef POLYTILE + polytile_remove(poly); +#endif + wpoly_move(poly, /*OUT*/ & wpoly_subsectors[subsecnum]); + // poly is empty +#ifdef POLYTILE + polytile_enter(&wpoly_subsectors[subsecnum]); +#endif +#ifdef DEBUG_TRACE + if (trigger_trace == 1) // only turn off specifc subsector traces + trigger_trace = 0; +#endif + +#ifdef HWR_LOADING_SCREEN + //Hurdler: implement a loading status + if (ls_count-- <= 0) + { + ls_count = numsubsectors/50; + loading_status(); + } +#endif return; } - bsp = &nodes[bspnum]; - SearchDivline(bsp, &fdivline); - SplitPoly(&fdivline, poly, &frontpoly, &backpoly); - poly = NULL; + // Node reference: bspnum is another node of the tree. + if ((size_t)bspnum >= numnodes) + goto bad_node; + bsp = &nodes[bspnum]; + set_divline(bsp, /*OUT*/ &fdivline); + wpoly_init_0(&frontpoly); + wpoly_init_0(&backpoly); +#ifdef POLYTILE + polytile_remove(poly); +#endif + SplitPoly (&fdivline, poly, &frontpoly, &backpoly); +#ifdef POLYTILE + polytile_enter(&frontpoly); + polytile_enter(&backpoly); +#endif + +#ifdef DEBUG_HWBSP //debug - if (!backpoly) - nobackpoly++; + if (backpoly.numpts == 0) + nobackpoly_cnt++; +#endif // Recursively divide front space. - if (frontpoly) + if (frontpoly.numpts) { - WalkBSPNode(bsp->children[0], frontpoly, &bsp->children[0],bsp->bbox[0]); +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-FRONT %i:\n", frontpoly.id1); + } +#endif + + HWR_WalkBSPNode(bsp->children[0], &frontpoly, &bsp->children[0], bsp->bbox[0]); // copy child bbox - memcpy(bbox, bsp->bbox[0], 4*sizeof (fixed_t)); + memcpy(bbox, bsp->bbox[0], 4*sizeof(fixed_t)); } else - I_Error("WalkBSPNode: no front poly?"); + { +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-FRONT %i EMPTY:\n", backpoly.id1); + } +#endif + + // [WDJ] Having no front poly is as likely as no back poly, since + // logic in Split Poly was changed to check poly direction. + // I_Error ("HWR_WalkBSPNode: no front poly, bspnum= %d\n", bspnum); // I_SoftError + } // Recursively divide back space. - if (backpoly) + if (backpoly.numpts) { +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-BACK %i:\n", backpoly.id1); + } +#endif // Correct back bbox to include floor/ceiling convex polygon - WalkBSPNode(bsp->children[1], backpoly, &bsp->children[1], bsp->bbox[1]); + HWR_WalkBSPNode(bsp->children[1], &backpoly, &bsp->children[1], bsp->bbox[1]); // enlarge bbox with second child M_AddToBox(bbox, bsp->bbox[1][BOXLEFT ], - bsp->bbox[1][BOXTOP ]); + bsp->bbox[1][BOXTOP ]); M_AddToBox(bbox, bsp->bbox[1][BOXRIGHT ], - bsp->bbox[1][BOXBOTTOM]); + bsp->bbox[1][BOXBOTTOM]); } + else + { +#ifdef DEBUG_TRACE + if (trigger_trace) + { + CONS_Debug(DBG_RENDER, "BSP-BACK %i EMPTY:\n", backpoly.id1); + } +#endif + } + + wpoly_free(&backpoly); + wpoly_free(&frontpoly); + return; + +bad_subsector: + I_Error("HWR_WalkBSPNode : bad secnum %i, numsectors=%lu -1\n", + subsecnum, numsubsectors); + +bad_node: + // frontpoly and backpoly are empty, and were not init. + I_Error("HWR_WalkBSPNode : bad node num %i, numnodes=%lu -1\n", + bspnum, numnodes); } -// FIXME: use Z_Malloc() STATIC ? -void HWR_FreeExtraSubsectors(void) +static void HWR_Freepolysubsectors(void) { - if (extrasubsectors) - free(extrasubsectors); - extrasubsectors = NULL; + Z_Free(poly_subsectors); + poly_subsectors = NULL; } -#define MAXDIST 1.5f -// BP: can't move vertex: DON'T change polygon geometry! (convex) +#define MAXDIST (1.5f) +// BP: can't move vertex : DON'T change polygon geometry ! (convex) //#define MOVEVERTEX -static boolean PointInSeg(polyvertex_t *a,polyvertex_t *v1,polyvertex_t *v2) +// Is vertex va within the seg v1, v2 +static boolean PointInSeg(polyvertex_t* va, polyvertex_t* v1, polyvertex_t* v2) { - register float ax,ay,bx,by,cx,cy,d,norm; - register polyvertex_t *p; + register float ax, ay, bx, by, cx, cy, d, norm; - // check bbox of the seg first - if (v1->x > v2->x) + // check bbox of the seg first (without altering v1, v2) + if (v2->x > v1->x) { - p = v1; - v1 = v2; - v2 = p; + // check if x within seg box v1..v2 + if ((va->x + MAXDIST) < v1->x) goto not_in; + if ((va->x - MAXDIST) > v2->x) goto not_in; + } + else + { + // check if x within seg box v2..v1 + if ((va->x + MAXDIST) < v2->x) goto not_in; + if ((va->x - MAXDIST) > v1->x) goto not_in; } - if (a->x < v1->x-MAXDIST || a->x > v2->x+MAXDIST) - return false; - - if (v1->y > v2->y) + if (v2->y > v1->y) { - p = v1; - v1 = v2; - v2 = p; + // check if x within seg box v1..v2 + if ((va->y + MAXDIST) < v1->y) goto not_in; + if ((va->y - MAXDIST) > v2->y) goto not_in; + } + else + { + // check if x within seg box v2..v1 + if ((va->y + MAXDIST) < v2->y) goto not_in; + if ((va->y - MAXDIST) > v1->y) goto not_in; } - if (a->y < v1->y-MAXDIST || a->y > v2->y+MAXDIST) - return false; - // v1 = origine - ax= v2->x-v1->x; - ay= v2->y-v1->y; - norm = (float)hypot(ax, ay); - ax /= norm; - ay /= norm; - bx = a->x-v1->x; - by = a->y-v1->y; - //d = a.b - d =ax*bx+ay*by; + // v1 = origin + ax = v2->x - v1->x; + ay = v2->y - v1->y; + norm = hypotf(ax, ay); // length of seg +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + if (norm != 0) // yes, this can be exactly 0 +#pragma GCC diagnostic pop + { + ax /= norm; + ay /= norm; // unit vector along seg, v1->v2 + } + bx = va->x - v1->x; + by = va->y - v1->y; // vector v1->va + + // d = (a DOT b), (product of lengths * cosine( angle )) + d = ax*bx+ay*by; // bound of the seg if (d < 0 || d > norm) + { + // Also excludes some va within MAXDIST of v1 or v2 + goto not_in; + } + + // Cross product. + if (((by * ax) - (bx * ay)) <= 0) + { + // The vertex is to the rightside of the seg, so adding + // it to the polygon would worsen the crack. return false; - //c = d.1a-b + } + + // measure the error in vector bx,by as difference squared sum + //c = (d * unit_vector_seg) - b cx = ax*d-bx; cy = ay*d-by; #ifdef MOVEVERTEX - if (cx*cx+cy*cy <= MAXDIST*MAXDIST) + if (cx*cx+cy*cy<=MAXDIST*MAXDIST) { - // ajust a little the point position - a->x = ax*d+v1->x; - a->y = ay*d+v1->y; - // anyway the correction is not enouth + // adjust a little the point position + a->x=ax*d+v1->x; + a->y=ay*d+v1->y; + // anyway the correction is not enough return true; } return false; #else return cx*cx+cy*cy <= MAXDIST*MAXDIST; #endif +not_in: + return false; } -static INT32 numsplitpoly; +// [WDJ] The poly forming code has attempted to improve the polygons. +// Snapping of divide points to existing vertexes is one, and it may +// contribute to cracks in the floor tiling. Snapping to an existing +// vertex may pull the edge of a polygon away from the adjoining polygon. +// Inaccuracies in the division lines may also contribute to this. +// This code attempts to find such cracks and fix the polygons to cover them. -static void SearchSegInBSP(INT32 bspnum,polyvertex_t *p,poly_t *poly) +static int num_T_vertex_fixed; + +// A structure to pass in BSP recursion, reducing it to one parameter. +typedef struct { + fixed_t max_x, min_x, max_y, min_y; + wpoly_t * poly; // our poly + polyvertex_t * pt; // T-split vertex + polyvertex_t * before, * after; // shared vertex before and after it + int our_secnum, find_secnum; // sectors + int pt_index; +} split_T_t; + +// Dist 0.4999 cures HOM in Freedoom map09 +#define SEARCHSEG_VERTEX_DIST 0.4999f + +// Recursive descent in BSP. +static void SearchSegInBSP(INT32 bspnum, split_T_t * stp) { - poly_t *q; - INT32 j,k; + wpoly_t *wq = stp->poly; + polyvertex_t *pt = stp->pt; + size_t subsecnum; + INT32 numpts, i1, i2; - if (bspnum & NF_SUBSECTOR) + const fixed_t maxy = stp->max_y; + const fixed_t maxx = stp->max_x; + const fixed_t miny = stp->min_y; + + for (;;) { - if (bspnum != -1) - { - bspnum &= ~NF_SUBSECTOR; - q = extrasubsectors[bspnum].planepoly; - if (poly == q || !q) - return; - for (j = 0; j < q->numpts; j++) - { - k = j+1; - if (k == q->numpts) k = 0; - if (!SameVertice(p, &q->pts[j]) - && !SameVertice(p, &q->pts[k]) - && PointInSeg(p, &q->pts[j], - &q->pts[k])) - { - poly_t *newpoly = HWR_AllocPoly(q->numpts+1); - INT32 n; + if (bspnum & NF_SUBSECTOR) + goto got_subsector; - for (n = 0; n <= j; n++) - newpoly->pts[n] = q->pts[n]; - newpoly->pts[k] = *p; - for (n = k+1; n < newpoly->numpts; n++) - newpoly->pts[n] = q->pts[n-1]; - numsplitpoly++; - extrasubsectors[bspnum].planepoly = - newpoly; - HWR_FreePoly(q); - return; - } - } - } - return; + const node_t *node = &nodes[bspnum]; + const fixed_t *bbox0 = nodes[bspnum].bbox[0]; + + const boolean left = + ((bbox0[BOXBOTTOM] <= maxy) & + (bbox0[BOXTOP] >= miny) & + (bbox0[BOXLEFT] <= maxx) & + (bbox0[BOXRIGHT] >= miny)); + + // Not a subsector, visit left and right children. + if (left) + SearchSegInBSP(node->children[0], stp); + + const fixed_t *bbox1 = nodes[bspnum].bbox[1]; + + const boolean right = + ((bbox1[BOXBOTTOM] <= maxy) & + (bbox1[BOXTOP] >= miny) & + (bbox1[BOXLEFT] <= maxx) & + (bbox1[BOXRIGHT] >= miny)); + + if (!right) + break; + + // Tail recursion within loop. + bspnum = node->children[1]; } - if ((FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXBOTTOM])-MAXDIST <= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXTOP ])+MAXDIST >= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXLEFT ])-MAXDIST <= p->x) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[0][BOXRIGHT ])+MAXDIST >= p->x)) - SearchSegInBSP(nodes[bspnum].children[0],p,poly); + return; - if ((FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXBOTTOM])-MAXDIST <= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXTOP ])+MAXDIST >= p->y) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXLEFT ])-MAXDIST <= p->x) && - (FIXED_TO_FLOAT(nodes[bspnum].bbox[1][BOXRIGHT ])+MAXDIST >= p->x)) - SearchSegInBSP(nodes[bspnum].children[1],p,poly); +got_subsector: + subsecnum = bspnum & ~NF_SUBSECTOR; + if (subsecnum >= numsubsectors) + return; + + // For every subsector polygon different than poly + wq = & wpoly_subsectors[subsecnum]; + if (wq == stp->poly || !wq) + return; + + numpts = wq->numpts; + if (numpts == 0) + return; + + // For all the vertex. + for (i1 = 0; i1 < numpts; i1++) + { + if (wq->ppts[i1] == pt) + continue; + + i2 = i1+1; + + if (i2 == numpts) + i2 = 0; + + if (wq->ppts[i2] == pt) + continue; + + if (PointInSeg(pt, wq->ppts[i1], wq->ppts[i2])) + { + goto add_pt; + } + } + // May have to search more than one polygon to find correct neighbor polygon. + return; + +add_pt: + // Insert the vertex pt into the polygon of the bsp subsector. + wpoly_insert_vert(pt, i2, /*INOUT*/ wq); + num_T_vertex_fixed++; + //stp->max_y = - 0x7ffffff0; // exit all the way + return; } // search for T-intersection problem -// BP : It can be mush more faster doing this at the same time of the splitpoly -// but we must use a different structure : polygone pointing on segs -// segs pointing on polygone and on vertex (too mush complicated, well not -// realy but i am soo lasy), the methode discibed is also better for segs presition -static INT32 SolveTProblem(void) -{ - poly_t *p; - INT32 i; - size_t l; +// BP : It can be much more faster doing this at the same time of the splitpoly +// but we must use a different structure : polygon pointing on segs +// segs pointing on polygon and on vertex (too much complicated, well not +// really but I am soo lazy), the method described is also better for segs precision - if (cv_glsolvetjoin.value == 0) - return 0; +static void SolveTProblem(void) +{ + split_T_t splitt; // parameter to search + wpoly_t *wp; + INT32 numpts, i, j; + size_t ssnum; + + if (!cv_glsolvetjoin.value) + return; CONS_Debug(DBG_RENDER, "Solving T-joins. This may take a while. Please wait...\n"); -#ifdef HWR_LOADING_SCREEN - CON_Drawer(); //let the user know what we are doing - I_FinishUpdate(); // page flip or blit buffer -#endif - numsplitpoly = 0; + num_T_vertex_fixed = 0; - for (l = 0; l < addsubsector; l++) + // For every subsector + for (ssnum = 0; ssnum < num_poly_subsector; ssnum++) { - p = extrasubsectors[l].planepoly; - if (p) - for (i = 0; i < p->numpts; i++) - SearchSegInBSP((INT32)numnodes-1, &p->pts[i], p); + wp = & wpoly_subsectors[ssnum]; + if (wp->numpts == 0) + continue; + + splitt.poly = wp; + numpts = wp->numpts; + + // For all vertex in the subsector + for (i = 0; i < numpts; i++) + { +#ifdef DEBUG_HWBSP + if (wp->ppts == NULL) + { + CONS_Debug(DBG_RENDER, "SolveT: NULL vertex, subsector= %d\n", ssnum); + } +#endif + // No need to process polyvertex from the level map. + if (in_poly_vert(wp->ppts[i])) + continue; + + // This is a vertex added by a split. + splitt.pt_index = i; + splitt.pt = wp->ppts[i]; + splitt.max_x = (fixed_t)((wp->ppts[i]->x + MAXDIST) * 0x10000); + splitt.min_x = (fixed_t)((wp->ppts[i]->x - MAXDIST) * 0x10000); + splitt.max_y = (fixed_t)((wp->ppts[i]->y + MAXDIST) * 0x10000); + splitt.min_y = (fixed_t)((wp->ppts[i]->y - MAXDIST) * 0x10000); + + j = i - 1; + + if (j < 0) + j += numpts; + + splitt.before = wp->ppts[j]; + + j = i + 1; + + if (j >= numpts) + j -= numpts; + + splitt.after = wp->ppts[j]; + + // Check added polyvertex due to SplitPoly. + SearchSegInBSP(numnodes-1, & splitt); + } } - //CONS_Debug(DBG_RENDER, "numsplitpoly %d\n", numsplitpoly); - return numsplitpoly; +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "DEBUG: SolveT: div polygon line= %d\n", num_T_vertex_fixed); +#endif } -#define NEARDIST (0.75f) -#define MYMAX (10000000000000.0f) +// lug: dont modify our actual sectors, that´ll break maps and causes desynchs! +//#define CHANGESUBSEC -/* Adjust true segs (from the segs lump) to be exactely the same as - * plane polygone segs - * This also convert fixed_t point of segs in float (in moste case - * it share the same vertice - */ +#ifdef CHANGESUBSEC +// [WDJ] Have a subsector poly with a suspect sector. +// Return the correct sector for the subsector poly. +static sector_t *find_poly_sector(wpoly_t *ssp) +{ + // Examine every linedef for the closest along the x or y axis. + // Use this linedef to assign the sector to the poly subsector. + + // There should be no two-sided linedefs actually within the subsector. + // If there were any one-sided linedefs within the subsector, they would + // have been segs, and would have decided the issue already. + polyvertex_t ap; // avg of subsector points + line_t *best_lp = NULL; + fixed_t best_dd = 0x7fffffff; + fixed_t px, py; + INT32 j; + size_t k; + + // Find an average point, not on a line, within the sector. + // If it is on the poly boundary, it can confuse the linedef exclusion tests. + ap.x = ap.y = 0.0; + for (j = 0; j < ssp->numpts; j++) + { + ap.x += ssp->ppts[j]->x; + ap.y += ssp->ppts[j]->y; + } + // Average of poly points. + ap.x /= ssp->numpts; + ap.y /= ssp->numpts; + px = (int)(ap.x * 0x10000); + py = (int)(ap.y * 0x10000); + +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "Find Poly Sector: (%6.2f,%6.2f)\n", ap.x, ap.y); +#endif + + // Find closest linedef to the test point, along x and y axis. + // Testing only one axis would work. + // But a linedef may be much closer along the other axis, so we test along both X and Y. + for (k = 0; k < numlines; k++) + { + line_t *lp = & lines[k]; + + if (lp->frontsector == lp->backsector) + continue; // self-ref lines lie. + + // Only consider linedef that bracket the test point. + fixed_t dx1 = px - lp->v1->x; + fixed_t dy1 = py - lp->v1->y; + fixed_t dx2 = px - lp->v2->x; + fixed_t dy2 = py - lp->v2->y; + + // (dx1 XOR dx2) < 0 means that one was < 0 and the other was > 0, + // which means it bracketed the point px,py. + + // Eqn of line: x = x1 + a * dx, y = y1 + a * dy + // At px: a = (px - x1) / dx + // dd = abs( (py - y1) - ( (px - x1) * dy / dx)) + if (((dx1 ^ dx2) < 0) && (lp->dx != 0)) // bracket px, and line not vert. + { + // Distance to line, measured along x-axis. + // This calc has a tendency to overflow, so use INT64. + INT64 dy3 = ((INT64)dx1) * lp->dy / lp->dx; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "FPS X: line= %i (%6.2f,%6.2f) to (%6.2f,%6.2f) dx,dy=(%6.2f,%6.2f) \n", + k, FIXED_TO_FLOAT(lp->v1->x), FIXED_TO_FLOAT(lp->v1->y), FIXED_TO_FLOAT(lp->v2->x), FIXED_TO_FLOAT(lp->v2->y), + FIXED_TO_FLOAT(lp->dx), FIXED_TO_FLOAT(lp->dy)); +#endif + if ((dy3 > INT32_MIN) && (dy3 < INT32_MAX)) // within fixed_t range + { + fixed_t dd = abs(dy1 - (fixed_t)dy3); +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "X dd=%6.2f\n", FIXED_TO_FLOAT(dd)); +#endif + if (dd < best_dd ) + { + best_dd = dd; + best_lp = lp; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "BEST=%i X dist=%i\n", k, dd>>16); +#endif + } + } + } + + if (((dy1 ^ dy2) < 0) && (lp->dy != 0)) // bracket py, and line not horz. + { + // Distance to line, measured along y-axis. + // This calc has a tendency to overflow, so use INT64. + INT64 dx3 = ((INT64)dy1) * lp->dx / lp->dy; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "FPS Y: line= %i (%6.2f,%6.2f) to (%6.2f,%6.2f) dx,dy=(%6.2f,%6.2f) \n", + k, FIXED_TO_FLOAT(lp->v1->x), FIXED_TO_FLOAT(lp->v1->y), FIXED_TO_FLOAT(lp->v2->x), FIXED_TO_FLOAT(lp->v2->y), + FIXED_TO_FLOAT(lp->dx), FIXED_TO_FLOAT(lp->dy)); +#endif + if ((dx3 > INT32_MIN) && (dx3 < INT32_MAX)) // within fixed_t range + { + fixed_t dd = abs(dx1 - (fixed_t)dx3); +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "Y dd=%6.2f\n", FIXED_TO_FLOAT(dd)); +#endif + if (dd < best_dd ) + { + best_dd = dd; + best_lp = lp; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "BEST=%i Y dist=%i\n", k, dd>>16); +#endif + } + } + } + } + + if (best_lp == NULL) + return NULL; + + // cross product with best_lp, to detect ap on rightside + double crpd = ((((double)(ap.x)) - FIXED_TO_FLOAT(best_lp->v1->x)) * FIXED_TO_FLOAT(best_lp->dy)) + - ((((double)(ap.y)) - FIXED_TO_FLOAT(best_lp->v1->y)) * FIXED_TO_FLOAT(best_lp->dx)); + + sector_t *fnd_sector = (crpd >= 0) + ? best_lp->frontsector // rightside of linedef + : best_lp->backsector; +#ifdef DEBUG_FPS + CONS_Debug(DBG_RENDER, "crpd=%6.2f sector=%i\n", crpd, fnd_sector - sectors); +#endif + return fnd_sector; +} +#endif + +#define VERTEX_NEAR_DIST (0.75f) +// Only needs to be reasonably larger than VERTEX_NEAR_DIST. +#define INITIAL_MAX (10000000000000.0f) +#define SEG_SAME_VERT (0.5f) + +// Adds polyvertex_t references to the segs. +// [WDJ] 2013/12 Removed writes of polyvertex_t* to vertex_t*, it now has its +// own ptrs. Fewer reverse conversions are needed. static void AdjustSegs(void) { - size_t i, count; +#ifdef DEBUG_HWBSP + int missed_seg_cnt = 0; + int fixed_segsec_cnt = 0; + int lost_segsec_cnt = 0; +#endif + INT16 segcount; + size_t ssnum; INT32 j; - seg_t *lseg; - poly_t *p; +#ifdef CHANGESUBSEC + sector_t *ss_sector, *poly_sector, *lseg_sector; +#endif + seg_t* lseg; + wpoly_t *wp; INT32 v1found = 0, v2found = 0; float nearv1, nearv2; - for (i = 0; i < numsubsectors; i++) + // for all segs in all sectors + for (ssnum = 0; ssnum < numsubsectors; ssnum++) { - count = subsectors[i].numlines; - lseg = &segs[subsectors[i].firstline]; - p = extrasubsectors[i].planepoly; - //if (!p) - //continue; - for (; count--; lseg++) + wp = & wpoly_subsectors[ssnum]; + if (wp->numpts == 0) + continue; + + segcount = subsectors[ssnum].numlines; +#ifdef CHANGESUBSEC + ss_sector = subsectors[ssnum].sector; +#endif + lseg = &segs[subsectors[ssnum].firstline]; +#ifdef CHANGESUBSEC + poly_sector = NULL; +#endif + for (; segcount--; lseg++) { - float distv1,distv2,tmp; - nearv1 = nearv2 = MYMAX; + polyvertex_t sv1, sv2; // seg v1, v2 + float distv1, distv2, tmp; // Don't touch polyobject segs. We'll compensate // for this when we go about drawing them. if (lseg->polyseg) continue; - if (p) { - for (j = 0; j < p->numpts; j++) + const line_t *line = lseg->linedef; + + if (!line) + continue; + + if (line->sidenum[1] != 0xffff) + { + if (sides[line->sidenum[0]].sector == sides[line->sidenum[1]].sector) { - distv1 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v1->x); - tmp = p->pts[j].y - FIXED_TO_FLOAT(lseg->v1->y); - distv1 = distv1*distv1+tmp*tmp; - if (distv1 <= nearv1) - { - v1found = j; - nearv1 = distv1; - } - // the same with v2 - distv2 = p->pts[j].x - FIXED_TO_FLOAT(lseg->v2->x); - tmp = p->pts[j].y - FIXED_TO_FLOAT(lseg->v2->y); - distv2 = distv2*distv2+tmp*tmp; - if (distv2 <= nearv2) - { - v2found = j; - nearv2 = distv2; - } + // Segs that are self-ref linedef do not cutout the subsector. +#ifdef DEBUG_HWBSP + CONS_Debug(DBG_RENDER, "AdjustSegs: self ref line %i\n", line - lines); +#endif + continue; } } - if (p && nearv1 <= NEARDIST*NEARDIST) - // share vertice with segs - lseg->pv1 = &(p->pts[v1found]); + +#ifdef CHANGESUBSEC + if (line->sidenum[lseg->side] != 0xffff) + { + // Get the sector from the seg. + lseg_sector = sides[line->sidenum[lseg->side]].sector; +#ifdef DEBUG_HWBSP + int secnum = lseg_sector - sectors; + if (lseg_sector != ss_sector) + CONS_Debug(DBG_RENDER, "AdjustSegs: seg line sector = %i, subsector sector = %i\n", + secnum, ss_sector - sectors); + if (poly_sector && (lseg_sector != poly_sector)) + CONS_Debug(DBG_RENDER, "AdjustSegs: seg line sector = %i, and %i\n", + secnum, poly_sector - sectors); +#endif + poly_sector = lseg_sector; + } +#endif + + sv1.x = FIXED_TO_FLOAT(lseg->v1->x); + sv1.y = FIXED_TO_FLOAT(lseg->v1->y); + sv2.x = FIXED_TO_FLOAT(lseg->v2->x); + sv2.y = FIXED_TO_FLOAT(lseg->v2->y); + + nearv1 = nearv2 = INITIAL_MAX; + // find nearest existing poly pts to seg v1, v2 + for (j = 0; j < wp->numpts; j++) + { + distv1 = wp->ppts[j]->x - sv1.x; + tmp = wp->ppts[j]->y - sv1.y; + distv1 = distv1*distv1 + tmp*tmp; + + if (distv1 <= nearv1) + { + v1found = j; + nearv1 = distv1; + } + + // the same with v2 + distv2 = wp->ppts[j]->x - sv2.x; + tmp = wp->ppts[j]->y - sv2.y; + distv2 = distv2*distv2 + tmp*tmp; + + if (distv2 <= nearv2) + { + v2found = j; + nearv2 = distv2; + } + } + + // close enough to be considered the same ? + if (nearv1 <= VERTEX_NEAR_DIST*VERTEX_NEAR_DIST) + { + // share vertex with segs + lseg->pv1 = wp->ppts[v1found]; + } else { // BP: here we can do better, using PointInSeg and compute - // the right point position also split a polygone side to - // solve a T-intersection, but too mush work + // the right point position also split a polygon side to + // solve a T-intersection, but too much work - // convert fixed vertex to float vertex - polyvertex_t *pv = HWR_AllocVertex(); - pv->x = FIXED_TO_FLOAT(lseg->v1->x); - pv->y = FIXED_TO_FLOAT(lseg->v1->y); - lseg->pv1 = pv; + lseg->pv1 = store_polyvertex(&sv1, SEG_SAME_VERT); + } + if (nearv2 <= VERTEX_NEAR_DIST*VERTEX_NEAR_DIST) + { + lseg->pv2 = wp->ppts[v2found]; } - if (p && nearv2 <= NEARDIST*NEARDIST) - lseg->pv2 = &(p->pts[v2found]); else { - polyvertex_t *pv = HWR_AllocVertex(); - pv->x = FIXED_TO_FLOAT(lseg->v2->x); - pv->y = FIXED_TO_FLOAT(lseg->v2->y); - lseg->pv2 = pv; + lseg->pv2 = store_polyvertex(&sv2, SEG_SAME_VERT); } // recompute length { - float x,y; - x = ((polyvertex_t *)lseg->pv2)->x - ((polyvertex_t *)lseg->pv1)->x - + FIXED_TO_FLOAT(FRACUNIT/2); - y = ((polyvertex_t *)lseg->pv2)->y - ((polyvertex_t *)lseg->pv1)->y - + FIXED_TO_FLOAT(FRACUNIT/2); - lseg->flength = (float)hypot(x, y); + const polyvertex_t *pv1 = (polyvertex_t *)lseg->pv1; + const polyvertex_t *pv2 = (polyvertex_t *)lseg->pv2; + + // [WDJ] FIXED_TO_FLOAT_MULT used to add 1/2 of lsb of fixed_t fraction. + float x = pv2->x - pv1->x + (0.5*FIXED_TO_FLOAT_MULT); + float y = pv2->y - pv1->y + (0.5*FIXED_TO_FLOAT_MULT); + lseg->flength = hypotf(x, y); + // BP: debug see this kind of segs - //if (nearv2 > NEARDIST*NEARDIST || nearv1 > NEARDIST*NEARDIST) - // lseg->length = 1; + //if (nearv2>VERTEX_NEAR_DIST*VERTEX_NEAR_DIST || nearv1>VERTEX_NEAR_DIST*VERTEX_NEAR_DIST) + //lseg->flength=1; } } + +#ifdef CHANGESUBSEC + // Fix bad subsector sector references. + if (poly_sector == NULL) + { + poly_sector = find_poly_sector(wp); +#ifdef DEBUG_HWBSP + lost_segsec_cnt++; +#endif + } + + if (poly_sector && (poly_sector != ss_sector)) + { + subsectors[ssnum].sector = poly_sector; +#ifdef DEBUG_HWBSP + fixed_segsec_cnt++; +#endif + } +#endif + } + + // check for missed segs, not in any polygon + for (j = 0; (size_t)j < numsegs; j++) + { + lseg = &segs[j]; + + // Don't touch polyobject segs. We'll compensate + // for this when we go about drawing them. + if (lseg->polyseg) + continue; + + if (!(lseg->pv1 && lseg->pv2)) + { + CONS_Debug(DBG_RENDER, "Seg %i, not in any polygon.\n", j); + } +#ifdef DEBUG_HWBSP + if ((!lseg->pv1) || (!lseg->pv2)) + missed_seg_cnt++; +#endif + if (!lseg->pv1) + { + lseg->pv1 = store_vertex(lseg->v1, SEG_SAME_VERT); + } + + if (!lseg->pv2) + { + lseg->pv2 = store_vertex(lseg->v2, SEG_SAME_VERT); + } + } +#ifdef DEBUG_HWBSP + + CONS_Debug(DBG_RENDER, "DEBUG: Lost seg sector cnt = %i\n", lost_segsec_cnt); + CONS_Debug(DBG_RENDER, "DEBUG: Fixed seg sector cnt = %i\n", fixed_segsec_cnt); + CONS_Debug(DBG_RENDER, "DEBUG: Missed seg vertex cnt = %i\n", missed_seg_cnt); +#endif +} + +// Generate drawing polygons from wpoly_t versions. +static void finalize_polygons(void) +{ + wpoly_t *wpoly; + poly_t *dpoly; // drawing poly + polyvertex_t *pv; + size_t ssnum; + INT16 ps; + + // For all segs in all sectors. + for (ssnum = 0; ssnum < numsubsectors; ssnum++) + { + wpoly = & wpoly_subsectors[ssnum]; + +#ifdef DEBUG_TRACE + if (trigger_subsector == 0xFFFFFFF2 || trigger_subsector == ssnum) + wpoly_dump( "Finalize:", wpoly); +#endif + + // Generate poly in poly_t format. + // Vertex in wpoly_t are ptr, but in poly_t they are a copy of the vertex. + dpoly = HWR_AllocPoly(wpoly->numpts); + poly_subsectors[ssnum].planepoly = dpoly; + pv = dpoly->pts; + + for ( ps = 0; ps < wpoly->numpts; ps++ ) + { + *pv++ = *(wpoly->ppts[ps]); // copy of each vertex + } } } - -// call this routine after the BSP of a Doom wad file is loaded, -// and it will generate all the convex polys for the hardware renderer +// Call this routine after the BSP of a Doom wad file is loaded, +// and it will generate all the convex polys for the hardware renderer. +// Called from P_SetupLevel void HWR_CreatePlanePolygons(INT32 bspnum) { - poly_t *rootp; - polyvertex_t *rootpv; + wpoly_t rootp; + polyvertex_t** rootpv; size_t i; fixed_t rootbbox[4]; + (void)bspnum; + CONS_Debug(DBG_RENDER, "Creating polygons, please wait...\n"); + #ifdef HWR_LOADING_SCREEN ls_count = ls_percent = 0; // reset the loading status CON_Drawer(); //let the user know what we are doing I_FinishUpdate(); // page flip or blit buffer #endif - HWR_ClearPolys(); - + // Enter all vertexes into the root bounding box. // find min/max boundaries of map - //CONS_Debug(DBG_RENDER, "Looking for boundaries of map...\n"); + CONS_Debug(DBG_RENDER, "Looking for boundaries of map...\n"); M_ClearBox(rootbbox); - for (i = 0;i < numvertexes; i++) + for (i = 0; i < numvertexes; i++) M_AddToBox(rootbbox, vertexes[i].x, vertexes[i].y); - //CONS_Debug(DBG_RENDER, "Generating subsector polygons... %d subsectors\n", numsubsectors); + CONS_Debug(DBG_RENDER, "Generating subsector polygons... %lu subsectors\n", numsubsectors); - HWR_FreeExtraSubsectors(); // allocate extra data for each subsector present in map - totsubsectors = numsubsectors + NEWSUBSECTORS; - extrasubsectors = calloc(totsubsectors, sizeof (*extrasubsectors)); - if (extrasubsectors == NULL) - I_Error("couldn't malloc extrasubsectors totsubsectors %s\n", sizeu1(totsubsectors)); + num_alloc_poly_subsector = numsubsectors + NUM_EXTRA_SUBSECTORS; + poly_subsectors = Z_Calloc(sizeof(poly_subsector_t) * num_alloc_poly_subsector, PU_STATIC, NULL); // set all data in to 0 or NULL !!! + + wpoly_subsectors = Z_Calloc(sizeof(wpoly_t) * num_alloc_poly_subsector, PU_HWRPLANE, NULL); // allocate table for back to front drawing of subsectors - /*gl_drawsubsectors = (INT16 *)malloc(sizeof (*gl_drawsubsectors) * totsubsectors); - if (!gl_drawsubsectors) - I_Error("couldn't malloc gl_drawsubsectors\n");*/ + /*gr_drawsubsectors = (short*)malloc(sizeof(*gr_drawsubsectors) * num_alloc_poly_subsector); + if (!gr_drawsubsectors) + I_Error("couldn't malloc gr_drawsubsectors\n");*/ + + // The level map polyvertexes + create_poly_vert(); // number of the first new subsector that might be added - addsubsector = numsubsectors; + num_poly_subsector = numsubsectors; // construct the initial convex poly that encloses the full map - rootp = HWR_AllocPoly(4); - rootpv = rootp->pts; + wpoly_init_alloc(4, &rootp); // alloc space for 4 pts +#ifdef DEBUG_TRACE + rootp.id1 = poly_id++; + rootp.id2 = 0; + rootp.id3 = 0; +#endif + rootp.numpts = 4; + rootpv = rootp.ppts; + rootpv[0] = new_polyvertex(); + rootpv[1] = new_polyvertex(); + rootpv[2] = new_polyvertex(); + rootpv[3] = new_polyvertex(); + // clockwise polygon + // 0=lower_left, 1=upper_left, 2=upper_right, 3=lower_right + rootpv[0]->x = rootpv[1]->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT ]); + rootpv[2]->x = rootpv[3]->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]); + rootpv[1]->y = rootpv[2]->y = FIXED_TO_FLOAT(rootbbox[BOXTOP ]); + rootpv[0]->y = rootpv[3]->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]); - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]); //lr - rootpv++; - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXTOP ]); //ur - rootpv++; - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXTOP ]); //ul - rootpv++; - rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]); - rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]); //ll - rootpv++; + // start at head node of bsp tree + // [WDJ] Intercept degenerate case, so BSP node is never -1. + HWR_WalkBSPNode(((numnodes > 0) ? numnodes-1 + : (0 | NF_SUBSECTOR)), // Degenerate, sector 0 + &rootp, NULL, rootbbox); - WalkBSPNode(bspnum, rootp, NULL,rootbbox); + SolveTProblem(); - i = SolveTProblem(); - //CONS_Debug(DBG_RENDER, "%d point divides a polygon line\n",i); AdjustSegs(); +#ifdef POLYTILE + polytile_clean(); +#endif + finalize_polygons(); // wpoly_t to drawing polygons + Z_Free(wpoly_subsectors); + wpoly_subsectors = NULL; + +#ifdef DEBUG_HWBSP //debug debug.. - //if (nobackpoly) - // CONS_Debug(DBG_RENDER, "no back polygon %u times\n",nobackpoly); - //"(should happen only with the deep water trick)" - //if (skipcut) - // CONS_Debug(DBG_RENDER, "%u cuts were skipped because of only one point\n",skipcut); + if (nobackpoly_cnt) + { + // should happen only with the deep water trick + CONS_Debug(DBG_RENDER, "DEBUG: no back polygon cnt= %d\n", nobackpoly_cnt); + } - //CONS_Debug(DBG_RENDER, "done: %u total subsector convex polygons\n", totalsubsecpolys); + if (skipcut_cnt) + CONS_Debug(DBG_RENDER, "DEBUG: cuts skipped because of only one point= %d\n", skipcut_cnt); + + CONS_Debug(DBG_RENDER, "DEBUG: total subsector convex polygons= %d\n", total_subsecpoly_cnt); +#endif } #endif //HWRENDER diff --git a/src/hardware/hw_plane.c b/src/hardware/hw_plane.c index 179895f1d..f4c862e5b 100644 --- a/src/hardware/hw_plane.c +++ b/src/hardware/hw_plane.c @@ -40,7 +40,7 @@ static FUINT HWR_CalcSlopeLight(FUINT lightnum, pslope_t *slope, const sector_t // -----------------+ // HWR_RenderPlane : Render a floor or ceiling convex polygon // -----------------+ -void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) +void HWR_RenderPlane(subsector_t *subsector, poly_subsector_t *xsub, boolean isceiling, fixed_t fixedheight, FBITFIELD PolyFlags, INT32 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { polyvertex_t * pv; float height; //constant y for all points on the convex flat polygon diff --git a/src/p_setup.c b/src/p_setup.c index 1465e718f..2631db202 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -9051,21 +9051,20 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Load the ccheckpoints and waypoints please! if (gametyperules & GTR_CIRCUIT && gamestate != GS_TITLESCREEN) { - - if ((K_SetupWaypointList() == false)) + if ((K_SetupWaypointList() == false)) + { + if (!K_UsingLegacyCheckpoints()) { - if (!K_UsingLegacyCheckpoints()) - { - CONS_Alert(CONS_ERROR, "Waypoints were not able to be setup and legacy checkpoints do not exist! Player positions will not work correctly.\n"); - } + CONS_Alert(CONS_ERROR, "Waypoints were not able to be setup and legacy checkpoints do not exist! Player positions will not work correctly.\n"); } + } } #ifdef HWRENDER // not win32 only 19990829 by Kin gl_maploaded = false; - // Lactozilla: Free extrasubsectors regardless of renderer. - HWR_FreeExtraSubsectors(); + // Lactozilla: Free poly_subsectors regardless of renderer. + HWR_FreePolyPool(); // Create plane polygons. if (rendermode == render_opengl)