quakeforge/tools/qfbsp/source/winding.c

322 lines
6.8 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
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 the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] =
"$Id$";
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <stdlib.h>
#include "QF/sys.h"
#include "bsp5.h"
#include "winding.h"
int c_activewindings, c_peakwindings;
winding_t *
BaseWindingForPlane (plane_t *p)
{
int i, x;
vec_t max, v;
vec3_t org, vright, vup;
winding_t *w;
// find the major axis
max = -BOGUS_RANGE;
x = -1;
for (i = 0; i < 3; i++) {
v = fabs (p->normal[i]);
if (v > max) {
x = i;
max = v;
}
}
if (x == -1)
Sys_Error ("BaseWindingForPlane: no axis found");
VectorZero (vup);
switch (x) {
case 0:
case 1:
vup[2] = 1;
break;
case 2:
vup[0] = 1;
break;
}
v = DotProduct (vup, p->normal);
VectorMultSub (vup, v, p->normal, vup);
_VectorNormalize (vup);
VectorScale (p->normal, p->dist, org);
CrossProduct (vup, p->normal, vright);
VectorScale (vup, BOGUS_RANGE, vup);
VectorScale (vright, BOGUS_RANGE, vright);
// project a really big axis aligned box onto the plane
w = NewWinding (4);
VectorSubtract (org, vright, w->points[0]);
VectorAdd (w->points[0], vup, w->points[0]);
VectorAdd (org, vright, w->points[1]);
VectorAdd (w->points[1], vup, w->points[1]);
VectorAdd (org, vright, w->points[2]);
VectorSubtract (w->points[2], vup, w->points[2]);
VectorSubtract (org, vright, w->points[3]);
VectorSubtract (w->points[3], vup, w->points[3]);
w->numpoints = 4;
return w;
}
winding_t *
CopyWinding (winding_t *w)
{
size_t size;
winding_t *c;
size = (size_t) (uintptr_t) &((winding_t *) 0)->points[w->numpoints];
c = malloc (size);
memcpy (c, w, size);
return c;
}
winding_t *
CopyWindingReverse (winding_t *w)
{
int i;
size_t size;
winding_t *c;
size = (size_t) (uintptr_t) &((winding_t *) 0)->points[w->numpoints];
c = malloc (size);
c->numpoints = w->numpoints;
for (i = 0; i < w->numpoints; i++) {
// add points backwards
VectorCopy (w->points[w->numpoints - 1 - i], c->points[i]);
}
return c;
}
/*
ClipWinding
Clips the winding to the plane, returning the new winding on the positive
side.
Frees the input winding.
If keepon is true, an exactly on-plane winding will be saved, otherwise
it will be clipped away.
*/
winding_t *
ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
{
int maxpts, i, j;
int *sides;
int counts[3];
vec_t dot;
vec_t *dists;
vec_t *p1, *p2;
vec3_t mid;
winding_t *neww;
counts[0] = counts[1] = counts[2] = 0;
sides = alloca ((in->numpoints + 1) * sizeof (int));
dists = alloca ((in->numpoints + 1) * sizeof (vec_t));
// determine sides for each point
for (i = 0; i < in->numpoints; i++) {
dot = DotProduct (in->points[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > ON_EPSILON)
sides[i] = SIDE_FRONT;
else if (dot < -ON_EPSILON)
sides[i] = SIDE_BACK;
else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK])
return in;
if (!counts[SIDE_FRONT]) {
FreeWinding (in);
return NULL;
}
if (!counts[SIDE_BACK])
return in;
for (maxpts = 0, i = 0; i < in->numpoints; i++) {
if (!(sides[i] & 1))
maxpts++;
if ((sides[i] ^ 1) == sides[i + 1])
maxpts++;
}
neww = NewWinding (maxpts);
for (i = 0; i < in->numpoints; i++) {
p1 = in->points[i];
if (sides[i] == SIDE_ON) {
if (neww->numpoints == maxpts)
Sys_Error ("ClipWinding: points exceeded estimate");
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT) {
if (neww->numpoints == maxpts)
Sys_Error ("ClipWinding: points exceeded estimate");
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
}
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
continue;
if (neww->numpoints == maxpts)
Sys_Error ("ClipWinding: points exceeded estimate");
// generate a split point
p2 = in->points[(i + 1) % in->numpoints];
dot = dists[i] / (dists[i] - dists[i + 1]);
for (j = 0; j < 3; j++) { // avoid round off error when possible
if (split->normal[j] == 1)
mid[j] = split->dist;
else if (split->normal[j] == -1)
mid[j] = -split->dist;
else
mid[j] = p1[j] + dot * (p2[j] - p1[j]);
}
VectorCopy (mid, neww->points[neww->numpoints]);
neww->numpoints++;
}
// free the original winding
FreeWinding (in);
return neww;
}
/*
DivideWinding
Divides a winding by a plane, producing one or two windings. The
original winding is not damaged or freed. If on only one side, the
returned winding will be the input winding. If on both sides, two
new windings will be created.
*/
void
DivideWinding (winding_t *in, plane_t *split, winding_t **front,
winding_t **back)
{
int i;
int counts[3];
plane_t plane;
vec_t dot;
winding_t *tmp;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i = 0; i < in->numpoints; i++) {
dot = DotProduct (in->points[i], split->normal) - split->dist;
if (dot > ON_EPSILON)
counts[SIDE_FRONT]++;
else if (dot < -ON_EPSILON)
counts[SIDE_BACK]++;
}
*front = *back = NULL;
if (!counts[SIDE_FRONT]) {
*back = in;
return;
}
if (!counts[SIDE_BACK]) {
*front = in;
return;
}
tmp = CopyWinding (in);
*front = ClipWinding (tmp, split, 0);
plane.dist = -split->dist;
VectorNegate (split->normal, plane.normal);
tmp = CopyWinding (in);
*back = ClipWinding (tmp, &plane, 0);
}
winding_t *
NewWinding (int points)
{
size_t size;
winding_t *w;
if (points < 3)
Sys_Error ("NewWinding: %i points", points);
c_activewindings++;
if (c_activewindings > c_peakwindings)
c_peakwindings = c_activewindings;
size = (size_t) (uintptr_t) &((winding_t *) 0)->points[points];
w = malloc (size);
memset (w, 0, size);
return w;
}
void
FreeWinding (winding_t *w)
{
c_activewindings--;
free (w);
}