this fixes numerous issues with holes in map geometry, misplaced sectors/planes, rare infinite loop hangs most notably fixes most rendering issues on UDMF/RR maps needs some testing as this may be slower than the old one
2973 lines
74 KiB
C
2973 lines
74 KiB
C
// BLANKART
|
||
//-----------------------------------------------------------------------------
|
||
// 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_map.c
|
||
/// \brief convert SRB2 map
|
||
|
||
#ifdef HWRENDER
|
||
|
||
#include <math.h>
|
||
|
||
#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 "../i_video.h"
|
||
|
||
#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
|
||
// --------------------------------------------------------------------------
|
||
|
||
// ---- Polygon vertexes
|
||
// Separate allocations for level map vertexes, and BSP vertexes.
|
||
|
||
// 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; i<poly->numpts; 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; i<poly->numpts; 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; i<poly->numpts; 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
|
||
{
|
||
float x, y;
|
||
float dx, dy;
|
||
} fdivline_t;
|
||
|
||
#ifdef DEBUG_HWBSP
|
||
static void fdivline_dump(const char * str, fdivline_t * dl)
|
||
{
|
||
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
|
||
|
||
typedef struct
|
||
{
|
||
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;
|
||
|
||
#ifdef DEBUG_HWBSP
|
||
static void divresult_dump( onst char * str, div_result_t * dr)
|
||
{
|
||
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");
|
||
}
|
||
#endif
|
||
|
||
// Return interception along bsp line (partline),
|
||
// with the polygon segment
|
||
|
||
// 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)
|
||
{
|
||
double frac;
|
||
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;
|
||
v1y = v1->y;
|
||
v1dx = v2->x - v1->x;
|
||
v1dy = v2->y - v1->y;
|
||
|
||
// the bsp partition line
|
||
v3x = partline->x;
|
||
v3y = partline->y;
|
||
v3dx = partline->dx;
|
||
v3dy = partline->dy;
|
||
|
||
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)
|
||
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
|
||
double num2 = (v3x - v1x)*v1dy + (v1y - v3y)*v1dx;
|
||
|
||
result->divfrac = num2 / den; // how far along partline vector
|
||
|
||
// [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;
|
||
|
||
// 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;
|
||
}
|
||
|
||
// 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)
|
||
{
|
||
// 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))
|
||
);
|
||
}
|
||
|
||
#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
|
||
//
|
||
// Called from: HWR_WalkBSPNode
|
||
static void SplitPoly(fdivline_t *dlnp, wpoly_t *poly, /*OUT*/ wpoly_t *frontpoly, wpoly_t *backpoly)
|
||
{
|
||
// 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;
|
||
|
||
#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++)
|
||
{
|
||
// i, j are one side of the poly
|
||
j = i+1;
|
||
if (j == poly->numpts)
|
||
j = 0; // wrap poly
|
||
|
||
// Find A and B points
|
||
dle = fracdivline(dlnp, poly->ppts[i], poly->ppts[j], /*OUT*/ result);
|
||
if (dle == DVL_none)
|
||
continue;
|
||
|
||
// have dividing pt
|
||
if (result == &A)
|
||
{
|
||
// Split at A
|
||
// Dependent upon dle, setup in fracdivline.
|
||
A.before += i;
|
||
A.after += i;
|
||
result = &B; // Setup to get crossing point B
|
||
continue;
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
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 (result == &A)
|
||
{
|
||
#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
|
||
}
|
||
else if (A.vertex == NULL)
|
||
{
|
||
#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)
|
||
{
|
||
// Found a crossing A point, but did not find a B crossing. Should not happen.
|
||
divresult_dump( " CROSSING-A", &A);
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
#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)
|
||
{
|
||
// Found A point at vertex, but did not find a B crossing. Just touched a vertex.
|
||
divresult_dump( " CROSSING-A", &A);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
// [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++)
|
||
{
|
||
// 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;
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
|
||
|
||
//#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)
|
||
{
|
||
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;
|
||
|
||
// 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 (;;)
|
||
{
|
||
sctp = seg_chain;
|
||
if (!sctp)
|
||
break;
|
||
|
||
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;
|
||
|
||
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
|
||
result = &A; // Setup to get crossing point A
|
||
|
||
for (i1 = 0; i1 < poly_num_pts; i1++)
|
||
{
|
||
i2 = i1 + 1;
|
||
if (i2 >= poly_num_pts)
|
||
i2 = 0;
|
||
|
||
// i1, i2 are one side of the poly
|
||
dle = fracdivline(&cutseg, poly->ppts[i1], poly->ppts[i2], /*OUT*/ result);
|
||
|
||
if (dle == DVL_none)
|
||
continue;
|
||
|
||
// have dividing pt
|
||
if (result == &A)
|
||
{
|
||
// Split at A
|
||
// Dependent upon dle, setup in fracdivline.
|
||
A.before += i1;
|
||
A.after += i1;
|
||
result = &B; // Setup to get crossing point B
|
||
continue;
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
// need 2 points
|
||
if (result == &B)
|
||
{
|
||
//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..)
|
||
// [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;
|
||
}
|
||
|
||
#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,
|
||
// so continue to cut off the poly into smaller parts with
|
||
// each seg of the subsector.
|
||
//
|
||
// 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)
|
||
{
|
||
#ifdef DEBUG_HWBSP
|
||
#ifdef DEBUG_TRACE
|
||
{
|
||
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 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->dx = FIXED_TO_FLOAT(bsp->dx);
|
||
divline->dy = FIXED_TO_FLOAT(bsp->dy);
|
||
}
|
||
|
||
#ifdef HWR_LOADING_SCREEN
|
||
//Hurdler: implement a loading status
|
||
static size_t ls_count = 0;
|
||
static UINT8 ls_percent = 0;
|
||
|
||
static void loading_status(void)
|
||
{
|
||
char s[16];
|
||
int x, y;
|
||
|
||
I_OsPolling();
|
||
CON_Drawer();
|
||
sprintf(s, "%d%%", (++ls_percent)<<1);
|
||
x = BASEVIDWIDTH/2;
|
||
y = BASEVIDHEIGHT/2;
|
||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); // Black background to match fade in effect
|
||
//V_DrawPatchFill(W_CachePatchName("SRB2BACK",PU_CACHE)); // SRB2 background, ehhh too bright.
|
||
M_DrawTextBox(x-58, y-8, 13, 1);
|
||
V_DrawString(x-50, y, V_YELLOWMAP, "Loading...");
|
||
V_DrawRightAlignedString(x+50, y, V_YELLOWMAP, s);
|
||
|
||
// Is this really necessary at this point..?
|
||
V_DrawCenteredString(BASEVIDWIDTH/2, 40, V_YELLOWMAP, "OPENGL MODE IS INCOMPLETE AND MAY");
|
||
V_DrawCenteredString(BASEVIDWIDTH/2, 50, V_YELLOWMAP, "NOT DISPLAY SOME SURFACES.");
|
||
V_DrawCenteredString(BASEVIDWIDTH/2, 70, V_YELLOWMAP, "USE AT SONIC'S RISK.");
|
||
|
||
I_UpdateNoVsync();
|
||
}
|
||
#endif
|
||
|
||
// poly : the convex polygon that encloses all child subsectors
|
||
// 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;
|
||
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)
|
||
{
|
||
// 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] )
|
||
{
|
||
CONS_Debug(DBG_RENDER, "BSP TRIGGER SECTOR %i: \n", trigger_bsp_sector);
|
||
trigger_trace = 1;
|
||
}
|
||
#endif
|
||
|
||
HWR_SubsecPoly(subsecnum, poly);
|
||
|
||
// Add the poly points into the bounding box.
|
||
M_ClearBox(bbox);
|
||
for (i = 0; i < poly->numpts; i++)
|
||
{
|
||
pt = poly->ppts[i];
|
||
M_AddToBox(bbox, (fixed_t)(pt->x * FRACUNIT), (fixed_t)(pt->y * FRACUNIT));
|
||
}
|
||
|
||
#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;
|
||
}
|
||
|
||
// 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.numpts == 0)
|
||
nobackpoly_cnt++;
|
||
#endif
|
||
|
||
// Recursively divide front space.
|
||
if (frontpoly.numpts)
|
||
{
|
||
#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));
|
||
}
|
||
else
|
||
{
|
||
#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.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
|
||
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 ]);
|
||
M_AddToBox(bbox, bsp->bbox[1][BOXRIGHT ],
|
||
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);
|
||
}
|
||
|
||
static void HWR_Freepolysubsectors(void)
|
||
{
|
||
Z_Free(poly_subsectors);
|
||
poly_subsectors = NULL;
|
||
}
|
||
|
||
#define MAXDIST (1.5f)
|
||
// BP: can't move vertex : DON'T change polygon geometry ! (convex)
|
||
//#define MOVEVERTEX
|
||
// 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;
|
||
|
||
// check bbox of the seg first (without altering v1, v2)
|
||
if (v2->x > v1->x)
|
||
{
|
||
// 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 (v2->y > v1->y)
|
||
{
|
||
// 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;
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
|
||
// 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)
|
||
{
|
||
// 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;
|
||
}
|
||
|
||
// [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 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)
|
||
{
|
||
wpoly_t *wq = stp->poly;
|
||
polyvertex_t *pt = stp->pt;
|
||
size_t subsecnum;
|
||
INT32 numpts, i1, i2;
|
||
|
||
const fixed_t maxy = stp->max_y;
|
||
const fixed_t maxx = stp->max_x;
|
||
const fixed_t miny = stp->min_y;
|
||
|
||
for (;;)
|
||
{
|
||
if (bspnum & NF_SUBSECTOR)
|
||
goto got_subsector;
|
||
|
||
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];
|
||
}
|
||
|
||
return;
|
||
|
||
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 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
|
||
|
||
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");
|
||
|
||
num_T_vertex_fixed = 0;
|
||
|
||
// For every subsector
|
||
for (ssnum = 0; ssnum < num_poly_subsector; ssnum++)
|
||
{
|
||
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);
|
||
}
|
||
}
|
||
#ifdef DEBUG_HWBSP
|
||
CONS_Debug(DBG_RENDER, "DEBUG: SolveT: div polygon line= %d\n", num_T_vertex_fixed);
|
||
#endif
|
||
}
|
||
|
||
// lug: dont modify our actual sectors, that´ll break maps and causes desynchs!
|
||
//#define CHANGESUBSEC
|
||
|
||
#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)
|
||
{
|
||
#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;
|
||
#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 all segs in all sectors
|
||
for (ssnum = 0; ssnum < numsubsectors; ssnum++)
|
||
{
|
||
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++)
|
||
{
|
||
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;
|
||
|
||
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)
|
||
{
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
#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 polygon side to
|
||
// solve a T-intersection, but too much work
|
||
|
||
lseg->pv1 = store_polyvertex(&sv1, SEG_SAME_VERT);
|
||
}
|
||
if (nearv2 <= VERTEX_NEAR_DIST*VERTEX_NEAR_DIST)
|
||
{
|
||
lseg->pv2 = wp->ppts[v2found];
|
||
}
|
||
else
|
||
{
|
||
lseg->pv2 = store_polyvertex(&sv2, SEG_SAME_VERT);
|
||
}
|
||
|
||
// recompute length
|
||
{
|
||
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>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.
|
||
// Called from P_SetupLevel
|
||
void HWR_CreatePlanePolygons(INT32 bspnum)
|
||
{
|
||
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
|
||
|
||
// Enter all vertexes into the root bounding box.
|
||
// find min/max boundaries of map
|
||
CONS_Debug(DBG_RENDER, "Looking for boundaries of map...\n");
|
||
M_ClearBox(rootbbox);
|
||
for (i = 0; i < numvertexes; i++)
|
||
M_AddToBox(rootbbox, vertexes[i].x, vertexes[i].y);
|
||
|
||
CONS_Debug(DBG_RENDER, "Generating subsector polygons... %lu subsectors\n", numsubsectors);
|
||
|
||
// allocate extra data for each subsector present in map
|
||
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
|
||
/*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
|
||
num_poly_subsector = numsubsectors;
|
||
|
||
// construct the initial convex poly that encloses the full map
|
||
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]);
|
||
|
||
// 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);
|
||
|
||
SolveTProblem();
|
||
|
||
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_cnt)
|
||
{
|
||
// should happen only with the deep water trick
|
||
CONS_Debug(DBG_RENDER, "DEBUG: no back polygon cnt= %d\n", nobackpoly_cnt);
|
||
}
|
||
|
||
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
|