2002-08-25 23:06:23 +00:00
|
|
|
/*
|
|
|
|
vis.c
|
|
|
|
|
|
|
|
PVS/PHS generation tool
|
|
|
|
|
2002-09-25 01:51:58 +00:00
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
2002-08-25 23:06:23 +00:00
|
|
|
Copyright (C) 2002 Colin Thompson
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
2003-01-15 15:31:36 +00:00
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_IO_H
|
|
|
|
# include <io.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
# include <string.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
# include <strings.h>
|
|
|
|
#endif
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
2004-02-02 05:44:46 +00:00
|
|
|
#include <ctype.h>
|
2002-09-12 06:48:05 +00:00
|
|
|
#ifdef HAVE_PTHREAD_H
|
|
|
|
# include <pthread.h>
|
|
|
|
#endif
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
#include "QF/bspfile.h"
|
2004-02-08 02:53:58 +00:00
|
|
|
#include "QF/cmd.h"
|
2021-01-01 09:58:26 +00:00
|
|
|
#include "QF/cmem.h"
|
2002-08-27 07:16:28 +00:00
|
|
|
#include "QF/dstring.h"
|
2002-08-25 23:06:23 +00:00
|
|
|
#include "QF/mathlib.h"
|
2002-08-27 07:16:28 +00:00
|
|
|
#include "QF/qtypes.h"
|
|
|
|
#include "QF/quakefs.h"
|
|
|
|
#include "QF/sys.h"
|
|
|
|
|
2020-06-21 14:15:17 +00:00
|
|
|
#include "tools/qfvis/include/vis.h"
|
|
|
|
#include "tools/qfvis/include/options.h"
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-18 04:31:35 +00:00
|
|
|
#ifdef USE_PTHREADS
|
2013-03-18 05:26:52 +00:00
|
|
|
pthread_attr_t threads_attrib;
|
|
|
|
pthread_rwlock_t *global_lock;
|
2013-03-18 06:03:11 +00:00
|
|
|
pthread_rwlock_t *portal_locks;
|
2013-03-18 05:26:52 +00:00
|
|
|
pthread_rwlock_t *stats_lock;
|
2002-08-25 23:06:23 +00:00
|
|
|
#endif
|
|
|
|
|
2002-09-19 05:35:17 +00:00
|
|
|
bsp_t *bsp;
|
|
|
|
|
2002-09-24 02:46:03 +00:00
|
|
|
options_t options;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-01-01 09:58:26 +00:00
|
|
|
static threaddata_t main_thread;
|
2021-03-28 11:58:29 +00:00
|
|
|
static visstat_t global_stats;
|
2013-03-19 02:42:09 +00:00
|
|
|
int base_mightsee;
|
2021-03-28 02:59:58 +00:00
|
|
|
unsigned base_spherecull;
|
|
|
|
unsigned base_windingcull;
|
2002-09-23 22:54:28 +00:00
|
|
|
|
2021-01-01 07:32:36 +00:00
|
|
|
static unsigned portal_count;
|
|
|
|
unsigned numportals;
|
|
|
|
unsigned portalclusters;
|
|
|
|
unsigned numrealleafs;
|
|
|
|
unsigned originalvismapsize;
|
2002-09-24 02:46:03 +00:00
|
|
|
int totalvis;
|
|
|
|
int count_sep;
|
|
|
|
int bitbytes; // (portalleafs + 63)>>3
|
|
|
|
int bitlongs;
|
2003-02-04 23:26:26 +00:00
|
|
|
int bitbytes_l; // (numrealleafs + 63)>>3
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-18 12:14:12 +00:00
|
|
|
portal_t **portal_queue;
|
|
|
|
|
2002-09-24 02:46:03 +00:00
|
|
|
portal_t *portals;
|
2003-02-04 23:26:26 +00:00
|
|
|
cluster_t *clusters;
|
2002-09-24 02:46:03 +00:00
|
|
|
dstring_t *visdata;
|
|
|
|
byte *uncompressed; // [bitbytes * portalleafs]
|
2003-02-04 23:26:26 +00:00
|
|
|
int *leafcluster; // leaf to cluster mappings as read from .prt file
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2010-11-21 00:56:31 +00:00
|
|
|
int *working; // per thread current portal #
|
|
|
|
|
2013-03-18 05:26:52 +00:00
|
|
|
static void
|
|
|
|
InitThreads (void)
|
|
|
|
{
|
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
if (pthread_attr_init (&threads_attrib) == -1)
|
|
|
|
Sys_Error ("pthread_attr_create failed");
|
|
|
|
if (pthread_attr_setstacksize (&threads_attrib, 0x100000) == -1)
|
|
|
|
Sys_Error ("pthread_attr_setstacksize failed");
|
|
|
|
|
|
|
|
global_lock = malloc (sizeof (pthread_rwlock_t));
|
|
|
|
if (pthread_rwlock_init (global_lock, 0))
|
|
|
|
Sys_Error ("pthread_rwlock_init failed");
|
|
|
|
|
|
|
|
stats_lock = malloc (sizeof (pthread_rwlock_t));
|
|
|
|
if (pthread_rwlock_init (stats_lock, 0))
|
|
|
|
Sys_Error ("pthread_rwlock_init failed");
|
2018-09-09 04:41:22 +00:00
|
|
|
#else
|
|
|
|
// Unable to run multi-threaded, so force threadcount to 1
|
|
|
|
options.threads = 1;
|
2013-03-18 05:26:52 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
EndThreads (void)
|
|
|
|
{
|
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
if (pthread_rwlock_destroy (global_lock) == -1)
|
|
|
|
Sys_Error ("pthread_rwlock_destroy failed");
|
|
|
|
free (global_lock);
|
|
|
|
if (pthread_rwlock_destroy (stats_lock) == -1)
|
|
|
|
Sys_Error ("pthread_rwlock_destroy failed");
|
|
|
|
free (stats_lock);
|
|
|
|
#endif
|
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
static vec4f_t
|
|
|
|
PlaneFromWinding (winding_t *winding)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2021-03-28 10:55:47 +00:00
|
|
|
vec4f_t plane;
|
|
|
|
vec4f_t v1, v2;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-19 07:23:47 +00:00
|
|
|
// calc plane using CW winding
|
2021-03-28 10:55:47 +00:00
|
|
|
v1 = winding->points[2] - winding->points[1];
|
|
|
|
v2 = winding->points[0] - winding->points[1];
|
|
|
|
plane = normalf (crossf (v2, v1));
|
|
|
|
// negative so dot(point, plane) includes -dist (point[3] = 1)
|
|
|
|
plane[3] = -dotf (winding->points[0], plane)[0];
|
|
|
|
return plane;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2002-09-24 02:46:03 +00:00
|
|
|
winding_t *
|
2021-01-01 09:58:26 +00:00
|
|
|
NewWinding (threaddata_t *thread, int points)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2002-09-24 02:46:03 +00:00
|
|
|
winding_t *winding;
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned size;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
if (points > MAX_POINTS_ON_WINDING)
|
2002-09-22 21:32:36 +00:00
|
|
|
Sys_Error ("NewWinding: %i points", points);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2018-09-09 04:38:32 +00:00
|
|
|
size = field_offset (winding_t, points[points]);
|
2021-01-01 09:58:26 +00:00
|
|
|
winding = CMEMALLOC (13, winding_t, thread->winding, thread->memsuper);
|
|
|
|
memset (winding, 0, size);
|
2021-03-27 14:04:13 +00:00
|
|
|
thread->stats.winding_alloc++;
|
2021-03-28 10:55:47 +00:00
|
|
|
winding->id = thread->winding_id++;
|
|
|
|
winding->thread = thread->id;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
return winding;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-01-01 09:58:26 +00:00
|
|
|
FreeWinding (threaddata_t *thread, winding_t *w)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2021-01-01 09:58:26 +00:00
|
|
|
if (!w->original) {
|
2021-03-27 14:04:13 +00:00
|
|
|
unsigned count = thread->stats.winding_alloc - thread->stats.winding_free;
|
|
|
|
if (count > thread->stats.winding_highwater) {
|
|
|
|
thread->stats.winding_highwater = count;
|
|
|
|
}
|
|
|
|
thread->stats.winding_free++;
|
2021-01-01 09:58:26 +00:00
|
|
|
CMEMFREE (thread->winding, w);
|
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2002-09-24 02:46:03 +00:00
|
|
|
winding_t *
|
2021-01-01 09:58:26 +00:00
|
|
|
CopyWinding (threaddata_t *thread, const winding_t *w)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned size;
|
2002-09-24 02:46:03 +00:00
|
|
|
winding_t *copy;
|
2002-09-19 02:37:52 +00:00
|
|
|
|
2018-09-09 04:38:32 +00:00
|
|
|
size = field_offset (winding_t, points[w->numpoints]);
|
2021-01-01 09:58:26 +00:00
|
|
|
copy = CMEMALLOC (13, winding_t, thread->winding, thread->memsuper);
|
2010-08-31 12:54:48 +00:00
|
|
|
memcpy (copy, w, size);
|
2002-09-19 02:37:52 +00:00
|
|
|
copy->original = false;
|
2021-03-27 14:04:13 +00:00
|
|
|
thread->stats.winding_alloc++;
|
2021-03-28 10:55:47 +00:00
|
|
|
copy->id = thread->winding_id++;
|
|
|
|
copy->thread = thread->id;
|
2002-09-19 02:37:52 +00:00
|
|
|
return copy;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2013-03-15 10:22:57 +00:00
|
|
|
static winding_t *
|
2021-01-01 09:58:26 +00:00
|
|
|
NewFlippedWinding (threaddata_t *thread, const winding_t *w)
|
2013-03-15 10:22:57 +00:00
|
|
|
{
|
|
|
|
winding_t *flipped;
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned i;
|
2013-03-15 10:22:57 +00:00
|
|
|
|
2021-01-01 09:58:26 +00:00
|
|
|
flipped = NewWinding (thread, w->numpoints);
|
2021-03-28 10:55:47 +00:00
|
|
|
for (i = 0; i < w->numpoints; i++) {
|
|
|
|
flipped->points[w->numpoints - 1 - i] = w->points[i];
|
|
|
|
}
|
2013-03-15 10:22:57 +00:00
|
|
|
flipped->numpoints = w->numpoints;
|
|
|
|
return flipped;
|
|
|
|
}
|
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
static vec4i_t
|
|
|
|
signeps (vec4f_t dist)
|
|
|
|
{
|
2021-05-24 06:02:18 +00:00
|
|
|
#ifdef __SSE3__
|
2021-03-28 10:55:47 +00:00
|
|
|
const vec4f_t zero = {};
|
|
|
|
const vec4f_t eps = { ON_EPSILON, ON_EPSILON, ON_EPSILON, ON_EPSILON };
|
|
|
|
vec4f_t d = _mm_addsub_ps (zero, dist);
|
|
|
|
vec4i_t c = (d - eps) > 0;
|
|
|
|
c = (vec4i_t) _mm_hsub_epi32 ((__m128i) c, (__m128i) c);
|
|
|
|
return c;
|
2021-05-24 06:02:18 +00:00
|
|
|
#else
|
|
|
|
float d = dist[0];
|
|
|
|
int front = (d >= ON_EPSILON);
|
|
|
|
int back = (d <= -ON_EPSILON);
|
|
|
|
int i = front - back;
|
|
|
|
return (vec4i_t) { i, i, i, i };
|
|
|
|
#endif
|
2021-03-28 10:55:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static vec4f_t
|
|
|
|
split_edge (const vec4f_t *points, const vec4f_t *dists,
|
|
|
|
int ind1, int ind2, vec4f_t split)
|
|
|
|
{
|
|
|
|
vec4f_t p1 = points[ind1];
|
|
|
|
vec4f_t p2 = points[ind2];
|
|
|
|
vec4f_t d1 = dists[ind1];
|
|
|
|
vec4f_t d2 = dists[ind2];
|
|
|
|
// avoid nan/inf in w: d1's w is never 0 (would not be here if it was)
|
|
|
|
// so the multiply ensures d1.w - d2.w cannot be 0 and thus d1.w/diff
|
|
|
|
// will not result in division by 0
|
|
|
|
static const vec4f_t one = { 1, 1, 1, 0 };
|
|
|
|
vec4f_t d = d1 / (d1 - d2 * one);
|
|
|
|
vec4f_t mid = p1 + d * (p2 - p1);
|
|
|
|
|
|
|
|
// avoid roundoff error when possible by forcing the appropriate
|
|
|
|
// component to the split-plane's distance when the split-plane's
|
|
|
|
// normal is signed-canonical.
|
|
|
|
// "nan" because 0x7fffffff is nan when viewed as a float
|
|
|
|
static const vec4f_t onenan = { 1, 1, 1, ~0u >> 1 };
|
|
|
|
static const vec4i_t nan = { ~0u >> 1, ~0u >> 1, ~0u >> 1, ~0u >> 1};
|
|
|
|
vec4i_t x = _mm_and_ps (split, (__m128) nan) == onenan;
|
|
|
|
// plane vector has -dist in w
|
|
|
|
vec4f_t y = _mm_and_ps (split, (__m128) x) * -split[3];
|
2021-05-24 06:02:18 +00:00
|
|
|
#ifdef __SSE3__
|
2021-03-28 10:55:47 +00:00
|
|
|
mid = _mm_blendv_ps (mid, y, (__m128) x);
|
2021-05-24 06:02:18 +00:00
|
|
|
#else
|
|
|
|
mid = (vec4f_t) ((vec4i_t) _mm_and_ps (y, (__m128) x) |
|
|
|
|
(vec4i_t) _mm_and_ps (mid, (__m128) ~x));
|
|
|
|
#endif
|
2021-03-28 10:55:47 +00:00
|
|
|
if (isnan (mid[0])) *(int *) 0 = 0;
|
|
|
|
return mid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_not_on (int x)
|
|
|
|
{
|
|
|
|
return x & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_back (int x)
|
|
|
|
{
|
|
|
|
return x & 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_not_back (unsigned x)
|
|
|
|
{
|
|
|
|
return ~x & 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_front (unsigned x)
|
|
|
|
{
|
|
|
|
return is_not_on (x) & (is_not_back (x) >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_back_front (unsigned x, unsigned y)
|
|
|
|
{
|
|
|
|
return (is_back (x) >> 1) & is_front (y);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_front_back (unsigned x, unsigned y)
|
|
|
|
{
|
|
|
|
return is_front (x) & (is_back (y) >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int __attribute__((const))
|
|
|
|
is_transition (unsigned x, unsigned y)
|
|
|
|
{
|
|
|
|
return is_back_front (x, y) | is_front_back (x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
test_point (vec4f_t split, const vec4f_t *points, int index, vec4f_t *dists,
|
|
|
|
int *sides, unsigned *counts)
|
|
|
|
{
|
|
|
|
dists[index] = dotf (points[index], split);
|
|
|
|
sides[index] = signeps (dists[index])[0];
|
|
|
|
counts[sides[index]]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef SIDE_FRONT
|
|
|
|
#undef SIDE_BACK
|
|
|
|
#undef SIDE_ON
|
|
|
|
#define SIDE_FRONT 1
|
|
|
|
#define SIDE_BACK -1
|
|
|
|
#define SIDE_ON 0
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
/*
|
|
|
|
ClipWinding
|
|
|
|
|
2002-08-26 15:05:23 +00:00
|
|
|
Clips the winding to the plane, returning the new winding on the positive
|
|
|
|
side
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
Frees the input winding.
|
2002-08-26 15:05:23 +00:00
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
If keepon is true, an exactly on-plane winding will be saved, otherwise
|
|
|
|
it will be clipped away.
|
|
|
|
*/
|
2002-09-24 02:46:03 +00:00
|
|
|
winding_t *
|
2021-03-28 10:55:47 +00:00
|
|
|
ClipWinding (threaddata_t *thread, winding_t *in, vec4f_t split,
|
|
|
|
qboolean keepon)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2021-03-28 10:55:47 +00:00
|
|
|
unsigned maxpts = 0;
|
|
|
|
unsigned i;
|
|
|
|
unsigned _counts[3];
|
|
|
|
unsigned *const counts = _counts + 1;
|
|
|
|
int *const sides = alloca ((in->numpoints + 1) * sizeof (int));
|
|
|
|
vec4f_t *const dists = alloca ((in->numpoints + 1) * sizeof (vec4f_t));
|
2002-09-24 02:46:03 +00:00
|
|
|
winding_t *neww;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
counts[SIDE_FRONT] = counts[SIDE_ON] = counts[SIDE_BACK] = 0;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
// determine sides for each point
|
2021-03-28 10:55:47 +00:00
|
|
|
test_point (split, in->points, 0, dists, sides, counts);
|
|
|
|
for (i = 1; i < in->numpoints; i++) {
|
|
|
|
test_point (split, in->points, i, dists, sides, counts);
|
|
|
|
maxpts += is_transition (sides[i - 1], sides[i]);
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
|
|
|
sides[i] = sides[0];
|
|
|
|
dists[i] = dists[0];
|
2021-03-28 10:55:47 +00:00
|
|
|
maxpts += is_transition (sides[i - 1], sides[i]);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
if (keepon && counts[SIDE_ON] == in->numpoints) {
|
2002-08-25 23:06:23 +00:00
|
|
|
return in;
|
2021-03-28 10:55:47 +00:00
|
|
|
}
|
|
|
|
if (!counts[SIDE_FRONT]) {
|
2021-01-01 09:58:26 +00:00
|
|
|
FreeWinding (thread, in);
|
2002-08-25 23:06:23 +00:00
|
|
|
return NULL;
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2021-03-28 10:55:47 +00:00
|
|
|
if (!counts[SIDE_BACK]) {
|
2002-08-25 23:06:23 +00:00
|
|
|
return in;
|
2021-03-28 10:55:47 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
maxpts += in->numpoints - counts[SIDE_BACK];
|
2021-01-01 09:58:26 +00:00
|
|
|
neww = NewWinding (thread, maxpts);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
for (i = 0; i < in->numpoints; i++) {
|
2002-08-25 23:06:23 +00:00
|
|
|
if (sides[i] == SIDE_ON) {
|
2021-03-28 10:55:47 +00:00
|
|
|
neww->points[neww->numpoints++] = in->points[i];
|
2002-08-25 23:06:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (sides[i] == SIDE_FRONT) {
|
2021-03-28 10:55:47 +00:00
|
|
|
neww->points[neww->numpoints++] = in->points[i];
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
2021-03-28 10:55:47 +00:00
|
|
|
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) {
|
2002-08-25 23:06:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-03-28 10:55:47 +00:00
|
|
|
vec4f_t mid = split_edge (in->points, dists, i,
|
|
|
|
(i + 1) % in->numpoints, split);
|
|
|
|
neww->points[neww->numpoints++] = mid;
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
if (neww->numpoints < maxpts) {
|
|
|
|
Sys_Error ("ClipWinding: not all points copied: n:%u m:%u i:%u %u %u %u",
|
|
|
|
neww->numpoints, maxpts, in->numpoints,
|
|
|
|
counts[SIDE_BACK], counts[SIDE_ON], counts[SIDE_FRONT]);
|
|
|
|
}
|
|
|
|
if (neww->numpoints > maxpts) {
|
|
|
|
Sys_Error ("ClipWinding: points exceeded estimate: n:%u m:%u",
|
|
|
|
neww->numpoints, maxpts);
|
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
// free the original winding
|
2021-01-01 09:58:26 +00:00
|
|
|
FreeWinding (thread, in);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
return neww;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static portal_t *
|
2021-01-01 07:36:01 +00:00
|
|
|
GetNextPortal (int limit)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2013-03-18 12:14:12 +00:00
|
|
|
portal_t *p = 0;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-01-01 07:36:01 +00:00
|
|
|
if (!(limit
|
|
|
|
&& options.portal_limit > 0
|
|
|
|
&& portal_count >= options.portal_limit)) {
|
|
|
|
WRLOCK (global_lock);
|
|
|
|
if (portal_count < 2 * numportals) {
|
|
|
|
p = portal_queue[portal_count++];
|
|
|
|
p->status = stat_selected;
|
|
|
|
}
|
|
|
|
UNLOCK (global_lock);
|
2003-09-12 23:13:11 +00:00
|
|
|
}
|
2002-09-19 02:37:52 +00:00
|
|
|
return p;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2013-03-09 05:32:51 +00:00
|
|
|
static void
|
2021-03-28 11:58:29 +00:00
|
|
|
UpdateMightsee (threaddata_t *thread, cluster_t *source, cluster_t *dest)
|
2013-03-09 05:32:51 +00:00
|
|
|
{
|
|
|
|
int i, clusternum;
|
|
|
|
portal_t *portal;
|
|
|
|
|
|
|
|
clusternum = dest - clusters;
|
|
|
|
for (i = 0; i < source->numportals; i++) {
|
|
|
|
portal = source->portals[i];
|
2013-03-18 06:03:11 +00:00
|
|
|
WRLOCK_PORTAL (portal);
|
|
|
|
if (portal->status == stat_none) {
|
|
|
|
if (set_is_member (portal->mightsee, clusternum)) {
|
|
|
|
set_remove (portal->mightsee, clusternum);
|
|
|
|
portal->nummightsee--;
|
2021-03-28 11:58:29 +00:00
|
|
|
thread->stats.mightseeupdate++;
|
2013-03-18 06:03:11 +00:00
|
|
|
}
|
2013-03-09 05:32:51 +00:00
|
|
|
}
|
2013-03-18 06:03:11 +00:00
|
|
|
UNLOCK_PORTAL (portal);
|
2013-03-09 05:32:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-28 11:58:29 +00:00
|
|
|
static void
|
|
|
|
UpdateStates (threaddata_t *thread)
|
|
|
|
{
|
|
|
|
WRLOCK (stats_lock);
|
|
|
|
global_stats.portaltest += thread->stats.portaltest;
|
|
|
|
global_stats.portalpass += thread->stats.portalpass;
|
|
|
|
global_stats.portalcheck += thread->stats.portalcheck;
|
|
|
|
global_stats.targettested += thread->stats.targettested;
|
|
|
|
global_stats.targettrimmed += thread->stats.targettrimmed;
|
|
|
|
global_stats.targetclipped += thread->stats.targetclipped;
|
|
|
|
global_stats.sourcetested += thread->stats.sourcetested;
|
|
|
|
global_stats.sourcetrimmed += thread->stats.sourcetrimmed;
|
|
|
|
global_stats.sourceclipped += thread->stats.sourceclipped;
|
|
|
|
global_stats.chains += thread->stats.chains;
|
|
|
|
global_stats.mighttest += thread->stats.mighttest;
|
|
|
|
global_stats.vistest += thread->stats.vistest;
|
|
|
|
global_stats.mightseeupdate += thread->stats.mightseeupdate;
|
|
|
|
global_stats.sep_alloc += thread->stats.sep_alloc;
|
|
|
|
global_stats.sep_free += thread->stats.sep_free;
|
|
|
|
global_stats.winding_alloc += thread->stats.winding_alloc;
|
|
|
|
global_stats.winding_free += thread->stats.winding_free;
|
|
|
|
global_stats.stack_alloc += thread->stats.stack_alloc;
|
|
|
|
global_stats.stack_free += thread->stats.stack_free;
|
|
|
|
UNLOCK (stats_lock);
|
|
|
|
memset (&thread->stats, 0, sizeof (thread->stats));
|
|
|
|
}
|
|
|
|
|
2013-03-09 05:32:51 +00:00
|
|
|
static void
|
2013-03-14 02:33:47 +00:00
|
|
|
PortalCompleted (threaddata_t *thread, portal_t *completed)
|
2013-03-09 05:32:51 +00:00
|
|
|
{
|
|
|
|
portal_t *portal;
|
|
|
|
cluster_t *cluster;
|
|
|
|
set_t *changed;
|
|
|
|
set_iter_t *ci;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
completed->status = stat_done;
|
2013-03-14 02:33:47 +00:00
|
|
|
|
2013-03-18 04:45:19 +00:00
|
|
|
changed = set_new_size_r (&thread->set_pool, portalclusters);
|
2013-03-09 05:32:51 +00:00
|
|
|
cluster = &clusters[completed->cluster];
|
|
|
|
for (i = 0; i < cluster->numportals; i++) {
|
|
|
|
portal = cluster->portals[i];
|
|
|
|
if (portal->status != stat_done)
|
|
|
|
continue;
|
|
|
|
set_assign (changed, portal->mightsee);
|
|
|
|
set_difference (changed, portal->visbits);
|
2021-03-28 12:11:13 +00:00
|
|
|
#if 0
|
|
|
|
dstring_copystr (thread->str, "mightsee: ");
|
|
|
|
set_as_string_r (thread->str, portal->mightsee);
|
|
|
|
dstring_appendstr (thread->str, "\nvisbits: ");
|
|
|
|
set_as_string_r (thread->str, portal->visbits);
|
|
|
|
dstring_appendstr (thread->str, "\nchanged: ");
|
|
|
|
set_as_string_r (thread->str, changed);
|
|
|
|
dstring_appendstr (thread->str, "\n");
|
|
|
|
write (0, thread->str->str, thread->str->size - 1);
|
|
|
|
#endif
|
2013-03-09 05:32:51 +00:00
|
|
|
for (j = 0; j < cluster->numportals; j++) {
|
|
|
|
if (j == i)
|
|
|
|
continue;
|
|
|
|
if (cluster->portals[j]->status == stat_done)
|
|
|
|
set_difference (changed, cluster->portals[j]->visbits);
|
|
|
|
else
|
|
|
|
set_difference (changed, cluster->portals[j]->mightsee);
|
|
|
|
}
|
2013-03-18 04:45:19 +00:00
|
|
|
for (ci = set_first_r (&thread->set_pool, changed); ci;
|
|
|
|
ci = set_next_r (&thread->set_pool, ci)) {
|
2021-03-28 11:58:29 +00:00
|
|
|
UpdateMightsee (thread, &clusters[ci->element], cluster);
|
2013-03-09 05:32:51 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-18 04:45:19 +00:00
|
|
|
set_delete_r (&thread->set_pool, changed);
|
2021-03-28 11:58:29 +00:00
|
|
|
|
|
|
|
UpdateStates (thread);
|
2013-03-09 05:32:51 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
static void
|
|
|
|
dump_super_stats (int id, memsuper_t *super)
|
|
|
|
{
|
2021-07-12 07:33:47 +00:00
|
|
|
size_t total_size = 0;
|
|
|
|
size_t total_allocated = 0;
|
2021-03-27 14:04:13 +00:00
|
|
|
size_t total_post_size = 0;
|
|
|
|
size_t total_post_allocated = 0;
|
|
|
|
size_t num_blocks = 0;
|
|
|
|
size_t free_counts[MAX_CACHE_LINES];
|
|
|
|
|
|
|
|
for (memblock_t *block = super->memblocks; block; block = block->next) {
|
|
|
|
num_blocks++;
|
2021-07-12 07:33:47 +00:00
|
|
|
total_size += block->size;
|
|
|
|
total_allocated += block->allocated;
|
2021-03-27 14:04:13 +00:00
|
|
|
total_post_size += block->post_size;
|
|
|
|
// post_free is a flag
|
|
|
|
total_post_allocated += !block->post_free * block->post_size;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < MAX_CACHE_LINES; i++) {
|
|
|
|
free_counts[i] = 0;
|
|
|
|
for (memline_t *line = super->free_lines[i]; line;
|
|
|
|
line = line->free_next) {
|
|
|
|
free_counts[i]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
WRLOCK (global_lock);
|
|
|
|
printf ("cmem stats for thread %d\n", id);
|
|
|
|
printf (" blocks: %zd\n", num_blocks);
|
2021-07-12 07:33:47 +00:00
|
|
|
printf (" : s:%-8zd a:%-8zd f:%-8zd\n", total_size,
|
|
|
|
total_allocated, total_size - total_allocated);
|
2021-03-27 14:04:13 +00:00
|
|
|
printf (" post: s:%-8zd a:%-8zd f:%-8zd\n", total_post_size,
|
|
|
|
total_post_allocated, total_post_size - total_post_allocated);
|
|
|
|
printf (" ");
|
|
|
|
for (int i = 0; i < MAX_CACHE_LINES; i++) {
|
|
|
|
printf (" %5d", 64 << i);
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
printf (" ");
|
|
|
|
for (int i = 0; i < MAX_CACHE_LINES; i++) {
|
|
|
|
printf (" %5zd", free_counts[i]);
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
UNLOCK (global_lock);
|
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void *
|
2010-11-21 00:56:31 +00:00
|
|
|
LeafThread (void *_thread)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2002-09-24 02:46:03 +00:00
|
|
|
portal_t *portal;
|
2010-11-21 00:56:31 +00:00
|
|
|
int thread = (int) (intptr_t) _thread;
|
2013-03-14 02:33:47 +00:00
|
|
|
threaddata_t data;
|
2021-03-27 14:04:13 +00:00
|
|
|
int count = 0;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-14 02:33:47 +00:00
|
|
|
memset (&data, 0, sizeof (data));
|
2013-03-18 04:30:50 +00:00
|
|
|
set_pool_init (&data.set_pool);
|
2021-03-27 14:04:13 +00:00
|
|
|
data.id = thread;
|
2021-01-01 09:58:26 +00:00
|
|
|
data.memsuper = new_memsuper ();
|
2021-03-28 12:11:13 +00:00
|
|
|
data.str = dstring_new ();
|
2002-09-19 02:37:52 +00:00
|
|
|
do {
|
2021-01-01 07:36:01 +00:00
|
|
|
portal = GetNextPortal (1);
|
2002-08-25 23:06:23 +00:00
|
|
|
if (!portal)
|
|
|
|
break;
|
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 3 && (!count++ % 16)) {
|
|
|
|
dump_super_stats (thread, data.memsuper);
|
|
|
|
}
|
2010-11-21 00:56:31 +00:00
|
|
|
if (working)
|
|
|
|
working[thread] = (int) (portal - portals);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-14 02:33:47 +00:00
|
|
|
PortalFlow (&data, portal);
|
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
int whw = data.stats.winding_highwater;
|
|
|
|
int shw = data.stats.sep_highwater;
|
|
|
|
int smb = data.stats.sep_maxbulk;
|
2013-03-14 02:33:47 +00:00
|
|
|
PortalCompleted (&data, portal);
|
2021-03-27 14:04:13 +00:00
|
|
|
data.stats.sep_highwater = shw;
|
|
|
|
data.stats.sep_maxbulk = smb;
|
|
|
|
data.stats.winding_highwater = whw;
|
2013-03-09 05:32:51 +00:00
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2021-01-01 07:32:36 +00:00
|
|
|
printf ("portal:%5i mightsee:%5i cansee:%5i %5u/%u\n",
|
2012-05-21 23:23:22 +00:00
|
|
|
(int) (portal - portals),
|
|
|
|
portal->nummightsee,
|
2013-03-07 09:51:23 +00:00
|
|
|
portal->numcansee,
|
|
|
|
portal_count, numportals * 2);
|
2002-09-19 02:37:52 +00:00
|
|
|
} while (1);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 2) {
|
|
|
|
printf ("thread %d winding highwater: %d\n", thread,
|
|
|
|
data.stats.winding_highwater);
|
|
|
|
printf ("thread %d separator highwater: %d\n", thread,
|
|
|
|
data.stats.sep_highwater);
|
|
|
|
printf ("thread %d separator maxbulk: %d\n", thread,
|
|
|
|
data.stats.sep_maxbulk);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.verbosity >= 4)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("thread %d done\n", thread);
|
2012-02-05 05:29:46 +00:00
|
|
|
if (working)
|
|
|
|
working[thread] = -1;
|
2021-01-01 09:58:26 +00:00
|
|
|
delete_memsuper (data.memsuper);
|
2021-03-28 12:11:13 +00:00
|
|
|
dstring_delete (data.str);
|
2002-09-19 02:37:52 +00:00
|
|
|
return NULL;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2013-03-19 02:42:09 +00:00
|
|
|
static void *
|
|
|
|
BaseVisThread (void *_thread)
|
|
|
|
{
|
|
|
|
portal_t *portal;
|
|
|
|
int thread = (int) (intptr_t) _thread;
|
|
|
|
basethread_t data;
|
|
|
|
set_pool_t set_pool;
|
|
|
|
int num_mightsee = 0;
|
|
|
|
|
|
|
|
memset (&data, 0, sizeof (data));
|
2021-03-27 14:04:13 +00:00
|
|
|
data.id = thread;
|
2013-03-19 02:42:09 +00:00
|
|
|
set_pool_init (&set_pool);
|
|
|
|
data.portalsee = set_new_size_r (&set_pool, numportals * 2);
|
|
|
|
do {
|
2021-01-01 07:36:01 +00:00
|
|
|
portal = GetNextPortal (0);
|
2013-03-19 02:42:09 +00:00
|
|
|
if (!portal)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (working)
|
|
|
|
working[thread] = (int) (portal - portals);
|
|
|
|
|
|
|
|
portal->mightsee = set_new_size_r (&set_pool, portalclusters);
|
|
|
|
set_empty (data.portalsee);
|
|
|
|
|
|
|
|
PortalBase (&data, portal);
|
|
|
|
num_mightsee += data.clustersee;
|
|
|
|
data.clustersee = 0;
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
WRLOCK (stats_lock);
|
2021-03-28 02:59:58 +00:00
|
|
|
base_spherecull += data.spherecull;
|
|
|
|
base_windingcull += data.windingcull;
|
2013-03-19 02:42:09 +00:00
|
|
|
base_mightsee += num_mightsee;
|
|
|
|
UNLOCK (stats_lock);
|
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("thread %d done\n", thread);
|
2013-03-19 02:42:09 +00:00
|
|
|
if (working)
|
|
|
|
working[thread] = -1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-18 04:31:35 +00:00
|
|
|
#ifdef USE_PTHREADS
|
2013-03-19 03:57:33 +00:00
|
|
|
const char spinner[] = "|/-\\";
|
|
|
|
const char progress[] = "0....1....2....3....4....5....6....7....8....9....";
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_thread_stats (const int *local_work, int thread, int spinner_ind)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < thread; i++)
|
|
|
|
printf ("%6d", local_work[i]);
|
2021-01-01 07:32:36 +00:00
|
|
|
printf (" %5u / %5u", portal_count, numportals * 2);
|
2013-03-19 03:57:33 +00:00
|
|
|
fflush (stdout);
|
|
|
|
printf (" %c\r", spinner[spinner_ind % 4]);
|
|
|
|
fflush (stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
print_progress (int prev_prog, int spinner_ind)
|
|
|
|
{
|
|
|
|
int prog;
|
|
|
|
|
|
|
|
prog = portal_count * 50 / (numportals * 2) + 1;
|
|
|
|
if (prog > prev_prog)
|
|
|
|
printf ("%.*s", prog - prev_prog, progress + prev_prog);
|
|
|
|
printf (" %c\b\b", spinner[spinner_ind % 4]);
|
|
|
|
fflush (stdout);
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
|
2021-07-26 13:42:03 +00:00
|
|
|
typedef struct {
|
|
|
|
int thread;
|
|
|
|
int (*progress)(int, int);
|
|
|
|
} watch_data_t;
|
|
|
|
|
2010-11-21 00:56:31 +00:00
|
|
|
static void *
|
2021-07-26 13:42:03 +00:00
|
|
|
WatchThread (void *_wd)
|
2010-11-21 00:56:31 +00:00
|
|
|
{
|
2021-07-26 13:42:03 +00:00
|
|
|
watch_data_t *wd = _wd;
|
|
|
|
int thread = wd->thread;
|
2010-11-21 00:56:31 +00:00
|
|
|
int *local_work = malloc (thread * sizeof (int));
|
|
|
|
int i;
|
2012-02-05 05:29:46 +00:00
|
|
|
int spinner_ind = 0;
|
|
|
|
int count = 0;
|
2013-03-19 03:57:33 +00:00
|
|
|
int prev_prog = 0;
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned prev_port = 0;
|
2013-03-19 03:57:33 +00:00
|
|
|
int stalled = 0;
|
2010-11-21 00:56:31 +00:00
|
|
|
|
|
|
|
while (1) {
|
2012-02-05 05:29:46 +00:00
|
|
|
usleep (1000);
|
2010-11-21 00:56:31 +00:00
|
|
|
for (i = 0; i < thread; i ++)
|
2012-02-05 05:29:46 +00:00
|
|
|
if (working[i] >= 0)
|
|
|
|
break;
|
|
|
|
if (i == thread)
|
|
|
|
break;
|
2013-03-19 03:57:33 +00:00
|
|
|
if (count++ == 100) {
|
2012-02-05 05:29:46 +00:00
|
|
|
count = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < thread; i ++)
|
|
|
|
local_work[i] = working[i];
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2013-03-19 03:57:33 +00:00
|
|
|
print_thread_stats (local_work, thread, spinner_ind);
|
2021-03-27 14:04:13 +00:00
|
|
|
else if (options.verbosity >= 0)
|
2021-07-26 13:42:03 +00:00
|
|
|
prev_prog = wd->progress (prev_prog, spinner_ind);
|
2013-03-19 03:57:33 +00:00
|
|
|
if (prev_port != portal_count || stalled++ == 10) {
|
|
|
|
prev_port = portal_count;
|
|
|
|
stalled = 0;
|
|
|
|
spinner_ind++;
|
|
|
|
}
|
2012-02-05 05:29:46 +00:00
|
|
|
}
|
2010-11-21 00:56:31 +00:00
|
|
|
}
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("watch thread done\n");
|
2021-03-27 14:04:13 +00:00
|
|
|
else if (options.verbosity >= 0)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("\n");
|
2013-03-07 04:42:19 +00:00
|
|
|
free (local_work);
|
2010-11-21 00:56:31 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-08-19 04:40:42 +00:00
|
|
|
#endif
|
2010-11-21 00:56:31 +00:00
|
|
|
|
2021-07-26 13:42:03 +00:00
|
|
|
void
|
|
|
|
RunThreads (void *(*thread_func) (void *), int (*progress)(int, int))
|
2013-03-18 12:11:46 +00:00
|
|
|
{
|
|
|
|
#ifdef USE_PTHREADS
|
2015-08-14 01:57:51 +00:00
|
|
|
pthread_t *work_threads;
|
2013-03-18 12:11:46 +00:00
|
|
|
void *status;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (options.threads > 1) {
|
2015-08-14 01:57:51 +00:00
|
|
|
work_threads = alloca ((options.threads + 1) * sizeof (pthread_t *));
|
2013-03-18 12:11:46 +00:00
|
|
|
working = calloc (options.threads, sizeof (int));
|
|
|
|
for (i = 0; i < options.threads; i++) {
|
|
|
|
if (pthread_create (&work_threads[i], &threads_attrib,
|
|
|
|
thread_func, (void *) (intptr_t) i) == -1)
|
|
|
|
Sys_Error ("pthread_create failed");
|
|
|
|
}
|
2021-07-26 13:42:03 +00:00
|
|
|
watch_data_t wd = { i, progress };
|
2013-03-18 12:11:46 +00:00
|
|
|
if (pthread_create (&work_threads[i], &threads_attrib,
|
2021-07-26 13:42:03 +00:00
|
|
|
WatchThread, &wd) == -1)
|
2013-03-18 12:11:46 +00:00
|
|
|
Sys_Error ("pthread_create failed");
|
|
|
|
|
|
|
|
for (i = 0; i < options.threads; i++) {
|
|
|
|
if (pthread_join (work_threads[i], &status) == -1)
|
|
|
|
Sys_Error ("pthread_join failed");
|
|
|
|
}
|
|
|
|
if (pthread_join (work_threads[i], &status) == -1)
|
|
|
|
Sys_Error ("pthread_join failed");
|
|
|
|
|
|
|
|
free (working);
|
|
|
|
} else {
|
|
|
|
thread_func (0);
|
|
|
|
}
|
|
|
|
#else
|
2021-07-26 13:42:03 +00:00
|
|
|
thread_func (0);
|
2013-03-18 12:11:46 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static int
|
2002-08-25 23:06:23 +00:00
|
|
|
CompressRow (byte *vis, byte *dest)
|
|
|
|
{
|
2004-02-08 02:53:58 +00:00
|
|
|
int rep, visrow, j;
|
2002-09-24 02:46:03 +00:00
|
|
|
byte *dest_p;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
dest_p = dest;
|
2003-02-04 23:26:26 +00:00
|
|
|
visrow = (numrealleafs + 7) >> 3;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
for (j = 0; j < visrow; j++) {
|
2002-08-25 23:06:23 +00:00
|
|
|
*dest_p++ = vis[j];
|
|
|
|
if (vis[j])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rep = 1;
|
|
|
|
for (j++; j < visrow; j++)
|
|
|
|
if (vis[j] || rep == 255)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
rep++;
|
|
|
|
*dest_p++ = rep;
|
|
|
|
j--;
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
return dest_p - dest;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
static void
|
2013-03-07 02:06:55 +00:00
|
|
|
ClusterFlowExpand (const set_t *src, byte *dest)
|
2003-02-04 23:26:26 +00:00
|
|
|
{
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned i, j;
|
2003-02-04 23:26:26 +00:00
|
|
|
|
|
|
|
for (j = 1, i = 0; i < numrealleafs; i++) {
|
2013-03-07 02:06:55 +00:00
|
|
|
if (set_is_member (src, leafcluster[i]))
|
2003-02-04 23:26:26 +00:00
|
|
|
*dest |= j;
|
|
|
|
j <<= 1;
|
|
|
|
if (j == 256) {
|
|
|
|
j = 1;
|
|
|
|
dest++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
/*
|
2003-02-04 23:26:26 +00:00
|
|
|
ClusterFlow
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
Builds the entire visibility list for a cluster
|
2002-08-25 23:06:23 +00:00
|
|
|
*/
|
|
|
|
void
|
2003-02-04 23:26:26 +00:00
|
|
|
ClusterFlow (int clusternum)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2013-03-07 02:06:55 +00:00
|
|
|
set_t *visclusters;
|
2021-03-20 03:00:40 +00:00
|
|
|
byte compressed[MAP_PVS_BYTES];
|
2004-02-08 02:53:58 +00:00
|
|
|
byte *outbuffer;
|
2013-03-07 02:06:55 +00:00
|
|
|
int numvis, i;
|
2003-02-04 23:26:26 +00:00
|
|
|
cluster_t *cluster;
|
2002-09-24 02:46:03 +00:00
|
|
|
portal_t *portal;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
outbuffer = uncompressed + clusternum * bitbytes_l;
|
|
|
|
cluster = &clusters[clusternum];
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
// flow through all portals, collecting visible bits
|
2003-02-04 23:26:26 +00:00
|
|
|
|
|
|
|
memset (compressed, 0, sizeof (compressed));
|
2013-03-07 02:06:55 +00:00
|
|
|
visclusters = set_new ();
|
2003-02-04 23:26:26 +00:00
|
|
|
for (i = 0; i < cluster->numportals; i++) {
|
|
|
|
portal = cluster->portals[i];
|
2002-08-25 23:06:23 +00:00
|
|
|
if (portal->status != stat_done)
|
2002-09-22 21:32:36 +00:00
|
|
|
Sys_Error ("portal not done");
|
2013-03-07 02:06:55 +00:00
|
|
|
set_union (visclusters, portal->visbits);
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-07 02:06:55 +00:00
|
|
|
if (set_is_member (visclusters, clusternum))
|
2003-02-04 23:26:26 +00:00
|
|
|
Sys_Error ("Cluster portals saw into cluster");
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-07 02:06:55 +00:00
|
|
|
set_add (visclusters, clusternum);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-07 02:06:55 +00:00
|
|
|
numvis = set_size (visclusters);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
// expand to cluster->leaf PVS
|
2013-03-07 02:06:55 +00:00
|
|
|
ClusterFlowExpand (visclusters, outbuffer);
|
2003-02-04 23:26:26 +00:00
|
|
|
|
2013-03-07 04:42:19 +00:00
|
|
|
set_delete (visclusters);
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
// compress the bit string
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2003-02-04 23:26:26 +00:00
|
|
|
printf ("cluster %4i : %4i visible\n", clusternum, numvis);
|
2002-09-19 02:37:52 +00:00
|
|
|
totalvis += numvis;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
i = CompressRow (outbuffer, compressed);
|
2003-03-17 00:52:16 +00:00
|
|
|
cluster->visofs = visdata->size;
|
2005-06-08 06:35:48 +00:00
|
|
|
dstring_append (visdata, (char *) compressed, i);
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2013-03-18 12:14:12 +00:00
|
|
|
static int
|
|
|
|
portalcmp (const void *_a, const void *_b)
|
|
|
|
{
|
|
|
|
portal_t *a = *(portal_t **) _a;
|
|
|
|
portal_t *b = *(portal_t **) _b;
|
|
|
|
return a->nummightsee - b->nummightsee;
|
|
|
|
}
|
|
|
|
|
2013-03-19 02:42:09 +00:00
|
|
|
static void
|
|
|
|
BasePortalVis (void)
|
|
|
|
{
|
|
|
|
double start, end;
|
|
|
|
|
2013-03-19 03:57:33 +00:00
|
|
|
if (options.verbosity >= 0)
|
|
|
|
printf ("Base vis: ");
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("\n");
|
2013-03-19 02:42:09 +00:00
|
|
|
|
2013-03-19 03:57:33 +00:00
|
|
|
start = Sys_DoubleTime ();
|
2021-07-26 13:42:03 +00:00
|
|
|
RunThreads (BaseVisThread, print_progress);
|
2013-03-19 02:42:09 +00:00
|
|
|
end = Sys_DoubleTime ();
|
2013-03-19 03:57:33 +00:00
|
|
|
|
2021-03-28 02:59:58 +00:00
|
|
|
if (options.verbosity >= 1) {
|
2013-03-19 02:42:09 +00:00
|
|
|
printf ("base_mightsee: %d %gs\n", base_mightsee, end - start);
|
2021-03-28 02:59:58 +00:00
|
|
|
printf ("sphere cull: %u winding cull %u\n",
|
|
|
|
base_spherecull, base_windingcull);
|
|
|
|
}
|
2013-03-19 02:42:09 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void
|
2002-08-25 23:06:23 +00:00
|
|
|
CalcPortalVis (void)
|
|
|
|
{
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned i;
|
2013-03-18 12:14:12 +00:00
|
|
|
double start, end;
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
// fastvis just uses mightsee for a very loose bound
|
2002-09-19 02:37:52 +00:00
|
|
|
if (options.minimal) {
|
2002-08-25 23:06:23 +00:00
|
|
|
for (i = 0; i < numportals * 2; i++) {
|
|
|
|
portals[i].visbits = portals[i].mightsee;
|
|
|
|
portals[i].status = stat_done;
|
|
|
|
}
|
|
|
|
return;
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2013-03-18 12:14:12 +00:00
|
|
|
start = Sys_DoubleTime ();
|
|
|
|
qsort (portal_queue, numportals * 2, sizeof (portal_t *), portalcmp);
|
|
|
|
end = Sys_DoubleTime ();
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 1)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("qsort: %gs\n", end - start);
|
|
|
|
|
|
|
|
if (options.verbosity >= 0)
|
|
|
|
printf ("Full vis: ");
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 4)
|
2013-03-19 03:57:33 +00:00
|
|
|
printf ("\n");
|
|
|
|
|
2021-01-01 07:36:01 +00:00
|
|
|
portal_count = 0;
|
|
|
|
|
2021-07-26 13:42:03 +00:00
|
|
|
RunThreads (LeafThread, print_progress);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 1) {
|
2002-08-26 15:05:23 +00:00
|
|
|
printf ("portalcheck: %i portaltest: %i portalpass: %i\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.portalcheck, global_stats.portaltest,
|
|
|
|
global_stats.portalpass);
|
2013-03-14 10:40:31 +00:00
|
|
|
printf ("target trimmed: %d clipped: %d tested: %d\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.targettrimmed, global_stats.targetclipped,
|
|
|
|
global_stats.targettested);
|
2013-03-14 10:40:31 +00:00
|
|
|
printf ("source trimmed: %d clipped: %d tested: %d\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.sourcetrimmed, global_stats.sourceclipped,
|
|
|
|
global_stats.sourcetested);
|
2013-03-14 10:40:31 +00:00
|
|
|
printf ("vistest: %i mighttest: %i mightseeupdate: %i\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.vistest, global_stats.mighttest,
|
|
|
|
global_stats.mightseeupdate);
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 2) {
|
|
|
|
printf ("separators allocated: %u freed: %u %u\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.sep_alloc, global_stats.sep_free,
|
|
|
|
global_stats.sep_alloc - global_stats.sep_free);
|
2021-03-27 14:04:13 +00:00
|
|
|
printf ("windings allocated: %u freed: %u %u\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.winding_alloc, global_stats.winding_free,
|
|
|
|
global_stats.winding_alloc - global_stats.winding_free);
|
2021-03-27 14:04:13 +00:00
|
|
|
printf ("stack blocks allocated: %u freed: %u %u\n",
|
2021-03-28 11:58:29 +00:00
|
|
|
global_stats.stack_alloc, global_stats.stack_free,
|
|
|
|
global_stats.stack_alloc - global_stats.stack_free);
|
2021-03-27 14:04:13 +00:00
|
|
|
}
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void
|
2002-08-25 23:06:23 +00:00
|
|
|
CalcVis (void)
|
|
|
|
{
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned i;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2018-09-09 04:41:00 +00:00
|
|
|
printf ("Thread count: %d\n", options.threads);
|
2002-09-19 02:37:52 +00:00
|
|
|
BasePortalVis ();
|
2021-03-28 11:59:46 +00:00
|
|
|
for (i = 0; i < 2 * numportals; i++) {
|
|
|
|
portals[i].status = stat_none;
|
|
|
|
}
|
2002-09-19 02:37:52 +00:00
|
|
|
CalcPortalVis ();
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
// assemble the leaf vis lists by oring and compressing the portal lists
|
2003-02-04 23:26:26 +00:00
|
|
|
for (i = 0; i < portalclusters; i++)
|
|
|
|
ClusterFlow (i);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
for (i = 0; i < numrealleafs; i++) {
|
2003-03-17 00:52:16 +00:00
|
|
|
bsp->leafs[i + 1].visofs = clusters[leafcluster[i]].visofs;
|
2003-02-04 23:26:26 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
if (options.verbosity >= 0)
|
2021-01-01 07:32:36 +00:00
|
|
|
printf ("average clusters visible: %u\n", totalvis / portalclusters);
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
2003-01-06 18:28:13 +00:00
|
|
|
#if 0
|
|
|
|
static qboolean
|
2002-08-25 23:06:23 +00:00
|
|
|
PlaneCompare (plane_t *p1, plane_t *p2)
|
|
|
|
{
|
2002-09-24 02:46:03 +00:00
|
|
|
int i;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
if (fabs (p1->dist - p2->dist) > 0.01)
|
2002-08-25 23:06:23 +00:00
|
|
|
return false;
|
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
for (i = 0; i < 3; i++)
|
2002-08-25 23:06:23 +00:00
|
|
|
if (fabs (p1->normal[i] - p2->normal[i]) > 0.001)
|
|
|
|
return false;
|
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
return true;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static sep_t *
|
2002-09-21 21:42:30 +00:00
|
|
|
FindPassages (winding_t *source, winding_t *pass)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2002-09-24 02:46:03 +00:00
|
|
|
double length;
|
|
|
|
float d;
|
|
|
|
int i, j, k, l;
|
2004-02-08 02:53:58 +00:00
|
|
|
int counts[3];
|
2002-09-24 02:46:03 +00:00
|
|
|
plane_t plane;
|
|
|
|
qboolean fliptest;
|
|
|
|
sep_t *sep, *list;
|
|
|
|
vec3_t v1, v2;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
list = NULL;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 05:35:17 +00:00
|
|
|
// check all combinations
|
2002-09-19 02:37:52 +00:00
|
|
|
for (i = 0; i < source->numpoints; i++) {
|
2002-08-25 23:06:23 +00:00
|
|
|
l = (i + 1) % source->numpoints;
|
|
|
|
VectorSubtract (source->points[l], source->points[i], v1);
|
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
// find a vertex of pass that makes a plane that puts all of the
|
2002-08-25 23:06:23 +00:00
|
|
|
// vertexes of pass on the front side and all of the vertexes of
|
|
|
|
// source on the back side
|
|
|
|
for (j = 0; j < pass->numpoints; j++) {
|
|
|
|
VectorSubtract (pass->points[j], source->points[i], v2);
|
|
|
|
|
|
|
|
plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
|
|
|
|
plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
|
|
|
|
plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
|
|
|
|
|
|
|
|
// if points don't make a valid plane, skip it
|
2012-05-21 23:23:22 +00:00
|
|
|
length = plane.normal[0] * plane.normal[0] +
|
|
|
|
plane.normal[1] * plane.normal[1] +
|
2002-08-25 23:06:23 +00:00
|
|
|
plane.normal[2] * plane.normal[2];
|
|
|
|
|
|
|
|
if (length < ON_EPSILON)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
length = 1 / sqrt(length);
|
|
|
|
|
|
|
|
plane.normal[0] *= length;
|
|
|
|
plane.normal[1] *= length;
|
|
|
|
plane.normal[2] *= length;
|
|
|
|
|
|
|
|
plane.dist = DotProduct (pass->points[j], plane.normal);
|
|
|
|
|
|
|
|
// find out which side of the generated seperating plane has the
|
|
|
|
// source portal
|
|
|
|
fliptest = false;
|
|
|
|
for (k = 0; k < source->numpoints; k++) {
|
|
|
|
if (k == i || k == l)
|
|
|
|
continue;
|
|
|
|
d = DotProduct (source->points[k], plane.normal) - plane.dist;
|
2012-05-21 23:23:22 +00:00
|
|
|
if (d < -ON_EPSILON) {
|
2002-08-25 23:06:23 +00:00
|
|
|
// source is on the negative side, so we want all
|
|
|
|
// pass and target on the positive side
|
|
|
|
fliptest = false;
|
|
|
|
break;
|
|
|
|
} else if (d > ON_EPSILON) {
|
|
|
|
// source is on the positive side, so we want all
|
|
|
|
// pass and target on the negative side
|
|
|
|
fliptest = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (k == source->numpoints)
|
|
|
|
continue; // planar with source portal
|
|
|
|
|
|
|
|
// flip the normal if the source portal is backwards
|
|
|
|
if (fliptest) {
|
2003-09-03 22:17:04 +00:00
|
|
|
VectorNegate (plane.normal, plane.normal);
|
2002-08-25 23:06:23 +00:00
|
|
|
plane.dist = -plane.dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if all of the pass portal points are now on the positive side,
|
|
|
|
// this is the seperating plane
|
|
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
|
|
for (k = 0; k < pass->numpoints; k++) {
|
|
|
|
if (k == j)
|
|
|
|
continue;
|
|
|
|
d = DotProduct (pass->points[k], plane.normal) - plane.dist;
|
|
|
|
if (d < -ON_EPSILON)
|
|
|
|
break;
|
|
|
|
else if (d > ON_EPSILON)
|
|
|
|
counts[0]++;
|
|
|
|
else
|
|
|
|
counts[2]++;
|
|
|
|
}
|
|
|
|
if (k != pass->numpoints)
|
|
|
|
continue; // points on negative side, not a seperating plane
|
|
|
|
|
|
|
|
if (!counts[0])
|
|
|
|
continue; // planar with pass portal
|
|
|
|
|
|
|
|
// save this out
|
|
|
|
count_sep++;
|
|
|
|
|
|
|
|
sep = malloc (sizeof (*sep));
|
|
|
|
sep->next = list;
|
|
|
|
list = sep;
|
|
|
|
sep->plane = plane;
|
|
|
|
}
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
|
|
|
return list;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2003-01-06 18:28:13 +00:00
|
|
|
static void
|
2002-08-25 23:06:23 +00:00
|
|
|
CalcPassages (void)
|
|
|
|
{
|
2002-09-24 02:46:03 +00:00
|
|
|
int count, count2, i, j, k;
|
|
|
|
leaf_t *leaf;
|
|
|
|
portal_t *p1, *p2;
|
|
|
|
sep_t *sep;
|
|
|
|
passage_t *passages;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
if (options.verbosity >= 0)
|
|
|
|
printf ("building passages...\n");
|
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
count = count2 = 0;
|
|
|
|
for (i = 0; i < portalleafs; i++) {
|
2002-08-25 23:06:23 +00:00
|
|
|
leaf = &leafs[i];
|
|
|
|
|
|
|
|
for (j = 0; j < leaf->numportals; j++) {
|
|
|
|
p1 = leaf->portals[j];
|
|
|
|
for (k = 0; k < leaf->numportals; k++) {
|
|
|
|
if (k == j)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
p2 = leaf->portals[k];
|
|
|
|
|
|
|
|
// definately can't see into a coplanar portal
|
|
|
|
if (PlaneCompare (&p1->plane, &p2->plane))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
count2++;
|
|
|
|
|
2002-09-21 21:42:30 +00:00
|
|
|
sep = FindPassages (p1->winding, p2->winding);
|
2002-08-25 23:06:23 +00:00
|
|
|
if (!sep) {
|
|
|
|
count_sep++;
|
|
|
|
sep = malloc (sizeof (*sep));
|
|
|
|
sep->next = NULL;
|
|
|
|
sep->plane = p1->plane;
|
|
|
|
}
|
|
|
|
passages = malloc (sizeof (*passages));
|
|
|
|
passages->planes = sep;
|
|
|
|
passages->from = p1->leaf;
|
|
|
|
passages->to = p2->leaf;
|
|
|
|
passages->next = leaf->passages;
|
|
|
|
leaf->passages = passages;
|
|
|
|
}
|
|
|
|
}
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
if (options.verbosity >= 0) {
|
|
|
|
printf ("numpassages: %i (%i)\n", count2, count);
|
|
|
|
printf ("total passages: %i\n", count_sep);
|
|
|
|
}
|
|
|
|
}
|
2003-01-06 18:28:13 +00:00
|
|
|
#endif
|
|
|
|
static void
|
2002-08-25 23:06:23 +00:00
|
|
|
LoadPortals (char *name)
|
|
|
|
{
|
2004-02-02 05:44:46 +00:00
|
|
|
const char *line;
|
|
|
|
char *err;
|
2021-01-01 07:32:36 +00:00
|
|
|
unsigned numpoints, i, j, k;
|
2004-02-02 05:44:46 +00:00
|
|
|
int read_leafs = 0;
|
2004-02-08 02:53:58 +00:00
|
|
|
int clusternums[2];
|
2003-02-04 23:26:26 +00:00
|
|
|
cluster_t *cluster;
|
2021-03-28 10:55:47 +00:00
|
|
|
vec4f_t plane;
|
2002-09-24 02:46:03 +00:00
|
|
|
portal_t *portal;
|
|
|
|
winding_t *winding;
|
2021-03-28 10:55:47 +00:00
|
|
|
vspheref_t sphere;
|
2004-02-08 02:53:58 +00:00
|
|
|
QFile *f;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
if (!strcmp (name, "-"))
|
2004-02-02 05:44:46 +00:00
|
|
|
f = Qdopen (0, "rt"); // create a QFile of stdin
|
2002-09-19 02:37:52 +00:00
|
|
|
else {
|
2004-02-02 05:44:46 +00:00
|
|
|
f = Qopen (name, "r");
|
2002-08-25 23:06:23 +00:00
|
|
|
if (!f) {
|
|
|
|
printf ("LoadPortals: couldn't read %s\n", name);
|
|
|
|
printf ("No vising performed.\n");
|
|
|
|
exit (1);
|
|
|
|
}
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2004-02-02 05:44:46 +00:00
|
|
|
line = Qgetline (f);
|
|
|
|
|
2004-03-19 00:41:14 +00:00
|
|
|
if (line && (!strcmp (line, PORTALFILE "\n")
|
|
|
|
|| !strcmp (line, PORTALFILE "\r\n"))) {
|
2004-02-02 05:44:46 +00:00
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &portalclusters) != 1)
|
2004-02-02 05:44:46 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &numportals) != 1)
|
2004-02-02 05:44:46 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
numrealleafs = portalclusters;
|
2004-03-19 00:41:14 +00:00
|
|
|
} else if (line && (!strcmp (line, PORTALFILE_AM "\n")
|
|
|
|
|| !strcmp (line, PORTALFILE_AM "\r\n"))) {
|
2004-02-02 05:44:46 +00:00
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &portalclusters) != 1)
|
2004-02-02 05:44:46 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &numportals) != 1)
|
2004-02-02 05:44:46 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &numrealleafs) != 1)
|
2004-02-02 05:44:46 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
read_leafs = 1;
|
2013-03-07 09:51:32 +00:00
|
|
|
} else if (line && (!strcmp (line, PORTALFILE2 "\n")
|
|
|
|
|| !strcmp (line, PORTALFILE2 "\r\n"))) {
|
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &numrealleafs) != 1)
|
2013-03-07 09:51:32 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &portalclusters) != 1)
|
2013-03-07 09:51:32 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
line = Qgetline (f);
|
2021-01-01 07:32:36 +00:00
|
|
|
if (!line || sscanf (line, "%u\n", &numportals) != 1)
|
2013-03-07 09:51:32 +00:00
|
|
|
Sys_Error ("LoadPortals: failed to read header");
|
|
|
|
read_leafs = 1;
|
2004-02-02 05:44:46 +00:00
|
|
|
} else {
|
2002-09-22 21:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: not a portal file");
|
2004-02-02 05:44:46 +00:00
|
|
|
}
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
if (options.verbosity >= 0) {
|
2021-01-01 07:32:36 +00:00
|
|
|
printf ("%4u portalclusters\n", portalclusters);
|
|
|
|
printf ("%4u numportals\n", numportals);
|
|
|
|
printf ("%4u numrealleafs\n", numrealleafs);
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
2012-05-21 23:23:22 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
bitbytes = ((portalclusters + 63) & ~63) >> 3;
|
2002-09-19 02:37:52 +00:00
|
|
|
bitlongs = bitbytes / sizeof (long);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
bitbytes_l = ((numrealleafs + 63) & ~63) >> 3;
|
|
|
|
|
2002-09-22 03:48:16 +00:00
|
|
|
// each file portal is split into two memory portals, one for each
|
|
|
|
// direction
|
2002-09-21 21:35:04 +00:00
|
|
|
portals = calloc (2 * numportals, sizeof (portal_t));
|
2013-03-18 12:14:12 +00:00
|
|
|
portal_queue = malloc (2 * numportals * sizeof (portal_t *));
|
|
|
|
for (i = 0; i < 2 * numportals; i++) {
|
|
|
|
portal_queue[i] = &portals[i];
|
|
|
|
}
|
2013-03-18 06:03:11 +00:00
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
portal_locks = calloc (2 * numportals, sizeof (pthread_rwlock_t));
|
|
|
|
for (i = 0; i < 2 * numportals; i++) {
|
|
|
|
if (pthread_rwlock_init (&portal_locks[i], 0))
|
|
|
|
Sys_Error ("pthread_rwlock_init failed");
|
|
|
|
}
|
|
|
|
#endif
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
clusters = calloc (portalclusters, sizeof (cluster_t));
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
originalvismapsize = numrealleafs * ((numrealleafs + 7) / 8);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
for (i = 0, portal = portals; i < numportals; i++) {
|
2004-02-02 05:44:46 +00:00
|
|
|
line = Qgetline (f);
|
|
|
|
if (!line)
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2004-02-02 05:44:46 +00:00
|
|
|
|
|
|
|
numpoints = strtol (line, &err, 10);
|
|
|
|
if (err == line)
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2004-02-02 05:44:46 +00:00
|
|
|
line = err;
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
clusternums[j] = strtol (line, &err, 10);
|
|
|
|
if (err == line)
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2004-02-02 05:44:46 +00:00
|
|
|
line = err;
|
|
|
|
}
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
if (numpoints > MAX_POINTS_ON_WINDING)
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: portal %u has too many points", i);
|
2003-04-17 00:01:48 +00:00
|
|
|
if ((unsigned) clusternums[0] > (unsigned) portalclusters
|
|
|
|
|| (unsigned) clusternums[1] > (unsigned) portalclusters)
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2021-01-01 09:58:26 +00:00
|
|
|
winding = portal->winding = NewWinding (&main_thread, numpoints);
|
2002-08-25 23:06:23 +00:00
|
|
|
winding->original = true;
|
|
|
|
winding->numpoints = numpoints;
|
|
|
|
|
|
|
|
for (j = 0; j < numpoints; j++) {
|
2004-02-02 05:44:46 +00:00
|
|
|
// (%ld %ld %ld)
|
2012-08-19 04:40:42 +00:00
|
|
|
while (isspace ((byte) *line))
|
2004-02-02 05:44:46 +00:00
|
|
|
line++;
|
|
|
|
if (*line++ != '(')
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2002-09-19 05:35:17 +00:00
|
|
|
|
2004-02-02 05:44:46 +00:00
|
|
|
for (k = 0; k < 3; k++) {
|
|
|
|
winding->points[j][k] = strtod (line, &err);
|
|
|
|
if (err == line)
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2004-02-02 05:44:46 +00:00
|
|
|
line = err;
|
|
|
|
}
|
2021-03-28 10:55:47 +00:00
|
|
|
winding->points[j][3] = 1;
|
2004-02-02 05:44:46 +00:00
|
|
|
|
2012-08-19 04:40:42 +00:00
|
|
|
while (isspace ((byte) *line))
|
2004-02-02 05:44:46 +00:00
|
|
|
line++;
|
|
|
|
if (*line++ != ')')
|
2021-01-01 07:32:36 +00:00
|
|
|
Sys_Error ("LoadPortals: reading portal %u", i);
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2021-03-28 10:55:47 +00:00
|
|
|
sphere = SmallestEnclosingBall_vf(winding->points, winding->numpoints);
|
|
|
|
//printf (VEC4F_FMT" %.9g\n", VEC4_EXP (sphere.center), sphere.radius);
|
|
|
|
|
2002-08-25 23:06:23 +00:00
|
|
|
// calc plane
|
2021-03-28 10:55:47 +00:00
|
|
|
plane = PlaneFromWinding (winding);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
// create forward portal
|
2003-02-04 23:26:26 +00:00
|
|
|
cluster = &clusters[clusternums[0]];
|
|
|
|
if (cluster->numportals == MAX_PORTALS_ON_CLUSTER)
|
|
|
|
Sys_Error ("Cluster with too many portals");
|
|
|
|
cluster->portals[cluster->numportals] = portal;
|
|
|
|
cluster->numportals++;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
portal->winding = winding;
|
2021-03-28 10:55:47 +00:00
|
|
|
portal->plane = -plane; // plane is for CW, portal is CCW
|
2003-02-04 23:26:26 +00:00
|
|
|
portal->cluster = clusternums[1];
|
2013-03-13 12:32:18 +00:00
|
|
|
portal->sphere = sphere;
|
2002-08-25 23:06:23 +00:00
|
|
|
portal++;
|
|
|
|
|
|
|
|
// create backwards portal
|
2003-02-04 23:26:26 +00:00
|
|
|
cluster = &clusters[clusternums[1]];
|
|
|
|
if (cluster->numportals == MAX_PORTALS_ON_CLUSTER)
|
|
|
|
Sys_Error ("Cluster with too many portals");
|
|
|
|
cluster->portals[cluster->numportals] = portal;
|
|
|
|
cluster->numportals++;
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-15 10:22:57 +00:00
|
|
|
// Use a flipped winding for the reverse portal so the winding
|
|
|
|
// direction and plane normal match.
|
2021-01-01 09:58:26 +00:00
|
|
|
portal->winding = NewFlippedWinding (&main_thread, winding);
|
2013-03-15 10:22:57 +00:00
|
|
|
portal->winding->original = true;
|
2002-08-25 23:06:23 +00:00
|
|
|
portal->plane = plane;
|
2003-02-04 23:26:26 +00:00
|
|
|
portal->cluster = clusternums[0];
|
2013-03-13 12:32:18 +00:00
|
|
|
portal->sphere = sphere;
|
2002-08-25 23:06:23 +00:00
|
|
|
portal++;
|
2002-09-19 02:37:52 +00:00
|
|
|
}
|
2003-02-04 23:26:26 +00:00
|
|
|
|
|
|
|
leafcluster = calloc (numrealleafs, sizeof (int));
|
2004-02-02 05:44:46 +00:00
|
|
|
if (read_leafs) {
|
2004-03-19 02:20:47 +00:00
|
|
|
for (i = 0; i < numrealleafs; i++) {
|
2004-02-02 05:44:46 +00:00
|
|
|
line = Qgetline (f);
|
|
|
|
if (sscanf (line, "%i\n", &leafcluster[i]) != 1)
|
|
|
|
Sys_Error ("LoadPortals: parse error in leaf->cluster "
|
|
|
|
"mappings");
|
2004-03-19 02:20:47 +00:00
|
|
|
}
|
2004-02-02 05:44:46 +00:00
|
|
|
} else {
|
|
|
|
for (i = 0; i < numrealleafs; i++)
|
|
|
|
leafcluster[i] = i;
|
|
|
|
}
|
|
|
|
Qclose (f);
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|
|
|
|
|
2021-07-26 13:42:03 +00:00
|
|
|
static void
|
|
|
|
generate_pvs (void)
|
2002-08-25 23:06:23 +00:00
|
|
|
{
|
2002-09-19 05:35:17 +00:00
|
|
|
QFile *f;
|
2012-05-21 23:23:22 +00:00
|
|
|
|
2021-07-26 13:42:03 +00:00
|
|
|
dstring_t *portalfile = dstring_new ();
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-20 19:03:06 +00:00
|
|
|
visdata = dstring_new ();
|
|
|
|
|
2010-08-24 07:20:07 +00:00
|
|
|
dstring_copystr (portalfile, options.bspfile->str);
|
|
|
|
QFS_SetExtension (portalfile, ".prt");
|
2002-09-12 06:48:05 +00:00
|
|
|
LoadPortals (portalfile->str);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2003-02-04 23:26:26 +00:00
|
|
|
uncompressed = calloc (bitbytes_l, portalclusters);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
|
|
|
CalcVis ();
|
|
|
|
|
2021-03-27 14:04:13 +00:00
|
|
|
if (options.verbosity >= 1)
|
2021-03-28 11:58:29 +00:00
|
|
|
printf ("chains: %i%s\n", global_stats.chains,
|
2002-09-21 03:51:50 +00:00
|
|
|
options.threads > 1 ? " (not reliable)" :"");
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2005-06-08 06:35:48 +00:00
|
|
|
BSP_AddVisibility (bsp, (byte *) visdata->str, visdata->size);
|
2002-08-25 23:06:23 +00:00
|
|
|
if (options.verbosity >= 0)
|
2010-12-23 02:40:16 +00:00
|
|
|
printf ("visdatasize:%ld compressed from %ld\n",
|
|
|
|
(long) bsp->visdatasize, (long) originalvismapsize);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
CalcAmbientSounds ();
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2010-08-24 07:20:07 +00:00
|
|
|
f = Qopen (options.bspfile->str, "wb");
|
2003-05-21 15:00:23 +00:00
|
|
|
if (!f)
|
2010-08-24 07:20:07 +00:00
|
|
|
Sys_Error ("couldn't open %s for writing.", options.bspfile->str);
|
2002-09-19 05:35:17 +00:00
|
|
|
WriteBSPFile (bsp, f);
|
|
|
|
Qclose (f);
|
2002-08-25 23:06:23 +00:00
|
|
|
|
2013-03-07 04:42:19 +00:00
|
|
|
dstring_delete (portalfile);
|
|
|
|
dstring_delete (visdata);
|
|
|
|
dstring_delete (options.bspfile);
|
|
|
|
free (leafcluster);
|
|
|
|
free (uncompressed);
|
|
|
|
free (portals);
|
|
|
|
free (clusters);
|
2021-07-26 13:42:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main (int argc, char **argv)
|
|
|
|
{
|
|
|
|
double start, stop;
|
|
|
|
QFile *f;
|
|
|
|
|
|
|
|
main_thread.memsuper = new_memsuper ();
|
|
|
|
|
|
|
|
start = Sys_DoubleTime ();
|
|
|
|
|
|
|
|
this_program = argv[0];
|
|
|
|
|
|
|
|
DecodeArgs (argc, argv);
|
|
|
|
|
|
|
|
InitThreads ();
|
|
|
|
|
|
|
|
if (!options.bspfile) {
|
|
|
|
usage (1);
|
|
|
|
Sys_Error ("%s: no bsp file specified.", this_program);
|
|
|
|
}
|
|
|
|
|
|
|
|
QFS_SetExtension (options.bspfile, ".bsp");
|
|
|
|
|
|
|
|
f = Qopen (options.bspfile->str, "rb");
|
|
|
|
if (!f)
|
|
|
|
Sys_Error ("couldn't open %s for reading.", options.bspfile->str);
|
|
|
|
bsp = LoadBSPFile (f, Qfilesize (f));
|
|
|
|
Qclose (f);
|
|
|
|
|
|
|
|
if (!options.no_auto_pvs) {
|
|
|
|
generate_pvs ();
|
|
|
|
}
|
|
|
|
if (options.fat_pvs) {
|
|
|
|
CalcFatPVS ();
|
|
|
|
}
|
|
|
|
|
|
|
|
BSP_Free (bsp);
|
2013-03-07 04:42:19 +00:00
|
|
|
|
2013-03-18 05:26:52 +00:00
|
|
|
EndThreads ();
|
|
|
|
|
2021-07-26 13:42:03 +00:00
|
|
|
stop = Sys_DoubleTime ();
|
|
|
|
|
|
|
|
if (options.verbosity >= -1)
|
|
|
|
printf ("%5.1f seconds elapsed\n", stop - start);
|
|
|
|
|
2002-09-19 02:37:52 +00:00
|
|
|
return 0;
|
2002-08-25 23:06:23 +00:00
|
|
|
}
|