quakeforge/tools/qfbsp/source/merge.c

256 lines
5.8 KiB
C
Raw Normal View History

2002-09-19 18:51:19 +00:00
/*
Copyright (C) 1996-1997 Id Software, Inc.
2002-09-19 18:51:19 +00:00
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.
2002-09-19 18:51:19 +00:00
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.
2002-09-19 18:51:19 +00:00
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
2002-09-19 18:51:19 +00:00
See file, 'COPYING', for details.
*/
2002-09-23 16:27:17 +00:00
static const char rcsid[] =
"$Id$";
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/sys.h"
#include "bsp5.h"
#define CONTINUOUS_EPSILON 0.001
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
CheckColinear (face_t *f)
{
2002-09-19 17:14:23 +00:00
int i, j;
vec3_t v1, v2;
for (i = 0; i < f->numpoints; i++) {
2002-09-23 16:27:17 +00:00
// skip the point if the vector from the previous point is the same as
// the vector to the next point
j = (i - 1 < 0) ? f->numpoints - 1 : i - 1;
VectorSubtract (f->pts[i], f->pts[j], v1);
_VectorNormalize (v1);
2002-09-19 17:14:23 +00:00
j = (i + 1 == f->numpoints) ? 0 : i + 1;
VectorSubtract (f->pts[j], f->pts[i], v2);
_VectorNormalize (v2);
2002-09-19 17:14:23 +00:00
if (_VectorCompare (v1, v2))
2002-09-19 17:14:23 +00:00
Sys_Error ("Colinear edge");
}
2002-09-19 17:14:23 +00:00
}
/*
2002-09-23 16:27:17 +00:00
TryMerge
2002-09-23 16:27:17 +00:00
If two polygons share a common edge and the edges that meet at the common
points are both inside the other polygons, merge them
2002-09-23 16:27:17 +00:00
Returns NULL if the faces couldn't be merged, or the new face.
The originals will NOT be freed.
*/
2002-09-19 17:14:23 +00:00
face_t *
2002-09-19 18:51:19 +00:00
TryMerge (face_t *f1, face_t *f2)
{
2002-09-19 17:14:23 +00:00
face_t *newf;
int i, j, k, l;
plane_t *plane;
qboolean keep1, keep2;
2002-09-19 18:51:19 +00:00
vec3_t normal, delta, planenormal;
vec_t dot;
vec_t *p1, *p2, *p3, *p4, *back;
2002-09-19 17:14:23 +00:00
if (f1->numpoints == -1 || f2->numpoints == -1)
return NULL;
if (f1->planeside != f2->planeside)
return NULL;
if (f1->texturenum != f2->texturenum)
return NULL;
if (f1->contents[0] != f2->contents[0])
return NULL;
if (f1->contents[1] != f2->contents[1])
return NULL;
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// find a common edge
2002-09-19 17:14:23 +00:00
p1 = p2 = NULL; // stop compiler warning
2002-09-19 18:51:19 +00:00
j = 0;
2002-09-19 17:14:23 +00:00
for (i = 0; i < f1->numpoints; i++) {
p1 = f1->pts[i];
2002-09-19 17:14:23 +00:00
p2 = f1->pts[(i + 1) % f1->numpoints];
for (j = 0; j < f2->numpoints; j++) {
p3 = f2->pts[j];
2002-09-19 17:14:23 +00:00
p4 = f2->pts[(j + 1) % f2->numpoints];
for (k = 0; k < 3; k++) {
if (fabs (p1[k] - p4[k]) > EQUAL_EPSILON)
break;
2002-09-19 17:14:23 +00:00
if (fabs (p2[k] - p3[k]) > EQUAL_EPSILON)
break;
}
2002-09-19 17:14:23 +00:00
if (k == 3)
break;
}
if (j < f2->numpoints)
break;
}
2002-09-19 17:14:23 +00:00
if (i == f1->numpoints)
2002-09-19 17:14:23 +00:00
return NULL; // no matching edges
2002-09-23 16:27:17 +00:00
// check slope of connected lines
// if the slopes are colinear, the point can be removed
plane = &planes[f1->planenum];
VectorCopy (plane->normal, planenormal);
if (f1->planeside)
VectorSubtract (vec3_origin, planenormal, planenormal);
2002-09-19 17:14:23 +00:00
back = f1->pts[(i + f1->numpoints - 1) % f1->numpoints];
VectorSubtract (p1, back, delta);
CrossProduct (planenormal, delta, normal);
_VectorNormalize (normal);
2002-09-19 17:14:23 +00:00
back = f2->pts[(j + 2) % f2->numpoints];
VectorSubtract (back, p1, delta);
dot = DotProduct (delta, normal);
if (dot > CONTINUOUS_EPSILON)
2002-09-19 17:14:23 +00:00
return NULL; // not a convex polygon
keep1 = dot < -CONTINUOUS_EPSILON;
2002-09-19 17:14:23 +00:00
back = f1->pts[(i + 2) % f1->numpoints];
VectorSubtract (back, p2, delta);
CrossProduct (planenormal, delta, normal);
_VectorNormalize (normal);
2002-09-19 17:14:23 +00:00
back = f2->pts[(j + f2->numpoints - 1) % f2->numpoints];
VectorSubtract (back, p2, delta);
dot = DotProduct (delta, normal);
if (dot > CONTINUOUS_EPSILON)
2002-09-19 17:14:23 +00:00
return NULL; // not a convex polygon
keep2 = dot < -CONTINUOUS_EPSILON;
2002-09-23 16:27:17 +00:00
// build the new polygon
2002-09-19 17:14:23 +00:00
if (f1->numpoints + f2->numpoints > MAXEDGES) {
2002-09-19 18:51:19 +00:00
// Sys_Error ("TryMerge: too many edges!");
return NULL;
}
newf = NewFaceFromFace (f1);
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// copy first polygon
2002-09-19 17:14:23 +00:00
for (k = (i + 1) % f1->numpoints; k != i; k = (k + 1) % f1->numpoints) {
if (k == (i + 1) % f1->numpoints && !keep2)
continue;
2002-09-19 17:14:23 +00:00
VectorCopy (f1->pts[k], newf->pts[newf->numpoints]);
newf->numpoints++;
}
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// copy second polygon
2002-09-19 17:14:23 +00:00
for (l = (j + 1) % f2->numpoints; l != j; l = (l + 1) % f2->numpoints) {
if (l == (j + 1) % f2->numpoints && !keep1)
continue;
VectorCopy (f2->pts[l], newf->pts[newf->numpoints]);
newf->numpoints++;
}
return newf;
}
2002-09-19 17:14:23 +00:00
qboolean mergedebug;
face_t *
2002-09-19 18:51:19 +00:00
MergeFaceToList (face_t *face, face_t *list)
{
2002-09-19 17:14:23 +00:00
face_t *newf, *f;
for (f = list; f; f = f->next) {
2002-09-19 18:51:19 +00:00
// CheckColinear (f);
2002-09-19 17:14:23 +00:00
if (mergedebug) {
Draw_ClearWindow ();
Draw_DrawFace (face);
Draw_DrawFace (f);
Draw_SetBlack ();
}
newf = TryMerge (face, f);
if (!newf)
continue;
FreeFace (face);
2002-09-19 17:14:23 +00:00
f->numpoints = -1; // merged out
return MergeFaceToList (newf, list);
}
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// didn't merge, so add at start
face->next = list;
return face;
}
2002-09-19 17:14:23 +00:00
face_t *
2002-09-19 18:51:19 +00:00
FreeMergeListScraps (face_t *merged)
{
2002-09-19 17:14:23 +00:00
face_t *head, *next;
head = NULL;
2002-09-19 17:14:23 +00:00
for (; merged; merged = next) {
next = merged->next;
if (merged->numpoints == -1)
FreeFace (merged);
2002-09-19 17:14:23 +00:00
else {
merged->next = head;
head = merged;
}
}
return head;
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
MergePlaneFaces (surface_t *plane)
{
2002-09-19 18:51:19 +00:00
face_t *merged, *next, *f1;
2002-09-19 17:14:23 +00:00
merged = NULL;
2002-09-19 17:14:23 +00:00
for (f1 = plane->faces; f1; f1 = next) {
next = f1->next;
merged = MergeFaceToList (f1, merged);
}
2002-09-23 16:27:17 +00:00
// chain all of the non-empty faces to the plane
plane->faces = FreeMergeListScraps (merged);
}
2002-09-19 17:14:23 +00:00
void
MergeAll (surface_t * surfhead)
{
2002-09-19 17:14:23 +00:00
face_t *f;
2002-09-19 18:51:19 +00:00
int mergefaces;
surface_t *surf;
2002-09-19 17:14:23 +00:00
printf ("---- MergeAll ----\n");
2002-09-19 17:14:23 +00:00
mergefaces = 0;
for (surf = surfhead; surf; surf = surf->next) {
MergePlaneFaces (surf);
2002-09-19 17:14:23 +00:00
Draw_ClearWindow ();
for (f = surf->faces; f; f = f->next) {
Draw_DrawFace (f);
mergefaces++;
}
}
2002-09-19 17:14:23 +00:00
printf ("%i mergefaces\n", mergefaces);
}