374 lines
9.3 KiB
C++
374 lines
9.3 KiB
C++
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
Copyright (C) 1997 Greg Lewis
|
|
|
|
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.
|
|
*/
|
|
// winding.c
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "qbsp.hh"
|
|
|
|
/*
|
|
=================
|
|
BaseWindingForPlane
|
|
=================
|
|
*/
|
|
winding_t *
|
|
BaseWindingForPlane(const 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;
|
|
}
|
|
}
|
|
|
|
VectorCopy(vec3_origin, vup);
|
|
switch (x) {
|
|
case 0:
|
|
case 1:
|
|
vup[2] = 1;
|
|
break;
|
|
case 2:
|
|
vup[0] = 1;
|
|
break;
|
|
default:
|
|
Error("Internal error: no axis for winding (%s)", __func__);
|
|
}
|
|
|
|
v = DotProduct(vup, p->normal);
|
|
VectorMA(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 = (winding_t *)AllocMem(WINDING, 4, true);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==================
|
|
CopyWinding
|
|
==================
|
|
*/
|
|
winding_t *
|
|
CopyWinding(const winding_t *w)
|
|
{
|
|
int size;
|
|
winding_t *c;
|
|
|
|
c = (winding_t *)AllocMem(WINDING, w->numpoints, false);
|
|
size = offsetof(winding_t, points[w->numpoints]);
|
|
memcpy(c, w, size);
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==================
|
|
CheckWinding
|
|
|
|
Check for possible errors
|
|
==================
|
|
*/
|
|
void
|
|
CheckWinding(const winding_t *w)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
CalcSides(const winding_t *in, const plane_t *split, int *sides, vec_t *dists,
|
|
int counts[3])
|
|
{
|
|
int i;
|
|
const vec_t *p;
|
|
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
p = in->points[0];
|
|
for (i = 0; i < in->numpoints; i++, p += 3) {
|
|
const vec_t dist = DotProduct(split->normal, p) - split->dist;
|
|
dists[i] = dist;
|
|
if (dist > ON_EPSILON)
|
|
sides[i] = SIDE_FRONT;
|
|
else if (dist < -ON_EPSILON)
|
|
sides[i] = SIDE_BACK;
|
|
else
|
|
sides[i] = SIDE_ON;
|
|
counts[sides[i]]++;
|
|
}
|
|
sides[i] = sides[0];
|
|
dists[i] = dists[0];
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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, const plane_t *split, bool keepon)
|
|
{
|
|
vec_t dists[MAX_POINTS_ON_WINDING + 1];
|
|
int sides[MAX_POINTS_ON_WINDING + 1];
|
|
int counts[3];
|
|
vec_t fraction;
|
|
int i, j;
|
|
vec_t *p1, *p2;
|
|
vec3_t mid;
|
|
winding_t *neww;
|
|
int maxpts;
|
|
|
|
if (in->numpoints > MAX_POINTS_ON_WINDING)
|
|
Error("Internal error: in->numpoints > MAX (%s: %d > %d)",
|
|
__func__, in->numpoints, MAX_POINTS_ON_WINDING);
|
|
|
|
CalcSides(in, split, sides, dists, counts);
|
|
|
|
if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK])
|
|
return in;
|
|
|
|
if (!counts[SIDE_FRONT]) {
|
|
FreeMem(in, WINDING, 1);
|
|
return NULL;
|
|
}
|
|
|
|
if (!counts[SIDE_BACK])
|
|
return in;
|
|
|
|
/* can't use maxpoints = counts[0] + 2 because of fp grouping errors */
|
|
maxpts = in->numpoints + 4;
|
|
neww = (winding_t *)AllocMem(WINDING, maxpts, true);
|
|
|
|
for (i = 0; i < in->numpoints; i++) {
|
|
p1 = in->points[i];
|
|
|
|
if (sides[i] == SIDE_ON) {
|
|
if (neww->numpoints == maxpts)
|
|
goto noclip;
|
|
VectorCopy(p1, neww->points[neww->numpoints]);
|
|
neww->numpoints++;
|
|
continue;
|
|
}
|
|
|
|
if (sides[i] == SIDE_FRONT) {
|
|
if (neww->numpoints == maxpts)
|
|
goto noclip;
|
|
VectorCopy(p1, neww->points[neww->numpoints]);
|
|
neww->numpoints++;
|
|
}
|
|
|
|
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
|
|
continue;
|
|
|
|
/* generate a split point */
|
|
p2 = in->points[(i + 1) % in->numpoints];
|
|
fraction = 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] + fraction * (p2[j] - p1[j]);
|
|
}
|
|
|
|
if (neww->numpoints == maxpts)
|
|
goto noclip;
|
|
VectorCopy(mid, neww->points[neww->numpoints]);
|
|
neww->numpoints++;
|
|
}
|
|
FreeMem(in, WINDING, 1);
|
|
|
|
return neww;
|
|
|
|
noclip:
|
|
Error("Internal error: new->numpoints > MAX (%s: %d > %d)",
|
|
__func__, neww->numpoints, maxpts);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
DivideWinding
|
|
|
|
Divides a winding by a plane, producing one or two windings. The
|
|
original winding is not damaged or freed. If only on 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, const plane_t *split, winding_t **front,
|
|
winding_t **back)
|
|
{
|
|
vec_t dists[MAX_POINTS_ON_WINDING + 1];
|
|
int sides[MAX_POINTS_ON_WINDING + 1];
|
|
int counts[3];
|
|
vec_t fraction;
|
|
int i, j;
|
|
vec_t *p1, *p2;
|
|
vec3_t mid;
|
|
winding_t *f, *b;
|
|
int maxpts;
|
|
|
|
if (in->numpoints > MAX_POINTS_ON_WINDING)
|
|
Error("Internal error: in->numpoints > MAX (%s: %d > %d)",
|
|
__func__, in->numpoints, MAX_POINTS_ON_WINDING);
|
|
|
|
CalcSides(in, split, sides, dists, counts);
|
|
|
|
*front = *back = NULL;
|
|
|
|
if (!counts[0]) {
|
|
*back = in;
|
|
return;
|
|
}
|
|
if (!counts[1]) {
|
|
*front = in;
|
|
return;
|
|
}
|
|
|
|
/* can't use maxpoints = counts[0] + 2 because of fp grouping errors */
|
|
maxpts = in->numpoints + 4;
|
|
*front = f = (winding_t *)AllocMem(WINDING, maxpts, true);
|
|
*back = b = (winding_t *)AllocMem(WINDING, maxpts, true);
|
|
|
|
for (i = 0; i < in->numpoints; i++) {
|
|
p1 = in->points[i];
|
|
|
|
if (sides[i] == SIDE_ON) {
|
|
if (f->numpoints == maxpts)
|
|
goto noclip_front;
|
|
VectorCopy(p1, f->points[f->numpoints]);
|
|
f->numpoints++;
|
|
if (b->numpoints == maxpts)
|
|
goto noclip_back;
|
|
VectorCopy(p1, b->points[b->numpoints]);
|
|
b->numpoints++;
|
|
continue;
|
|
}
|
|
|
|
if (sides[i] == SIDE_FRONT) {
|
|
if (f->numpoints == maxpts)
|
|
goto noclip_front;
|
|
VectorCopy(p1, f->points[f->numpoints]);
|
|
f->numpoints++;
|
|
}
|
|
if (sides[i] == SIDE_BACK) {
|
|
if (b->numpoints == maxpts)
|
|
goto noclip_back;
|
|
VectorCopy(p1, b->points[b->numpoints]);
|
|
b->numpoints++;
|
|
}
|
|
|
|
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
|
|
continue;
|
|
|
|
/* generate a split point */
|
|
p2 = in->points[(i + 1) % in->numpoints];
|
|
|
|
fraction = 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] + fraction * (p2[j] - p1[j]);
|
|
}
|
|
|
|
if (f->numpoints == maxpts)
|
|
goto noclip_front;
|
|
VectorCopy(mid, f->points[f->numpoints]);
|
|
f->numpoints++;
|
|
if (b->numpoints == maxpts)
|
|
goto noclip_back;
|
|
VectorCopy(mid, b->points[b->numpoints]);
|
|
b->numpoints++;
|
|
}
|
|
return;
|
|
|
|
noclip_front:
|
|
Error("Internal error: front->numpoints > MAX (%s: %d > %d)",
|
|
__func__, f->numpoints, maxpts);
|
|
noclip_back:
|
|
Error("Internal error: back->numpoints > MAX (%s: %d > %d)",
|
|
__func__, b->numpoints, maxpts);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
MidpointWinding
|
|
==================
|
|
*/
|
|
void
|
|
MidpointWinding(const winding_t *w, vec3_t v)
|
|
{
|
|
int i;
|
|
|
|
VectorCopy(vec3_origin, v);
|
|
for (i = 0; i < w->numpoints; i++)
|
|
VectorAdd(v, w->points[i], v);
|
|
if (w->numpoints)
|
|
VectorScale(v, 1.0 / w->numpoints, v);
|
|
}
|